commit 62d68374e1573ca86cdfc70539f6619677a01ebc Author: Wu Date: Sat Dec 19 17:14:40 2020 +0000 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d67791 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +uploads/ \ No newline at end of file diff --git a/app/config/multer.config.js b/app/config/multer.config.js new file mode 100644 index 0000000..41f91a6 --- /dev/null +++ b/app/config/multer.config.js @@ -0,0 +1,19 @@ +const multer = require('multer'); +const fs = require('fs'); + +var storage = multer.diskStorage({ + destination: (req, file, cb) => { + var dir = __basedir + '/uploads/' + file.originalname.split('.').slice(0, -1).join('.'); + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); + } + cb(null, __basedir + '/uploads/' + file.originalname.split('.').slice(0, -1).join('.')) + }, + filename: (req, file, cb) => { + cb(null, 'my_model.py'); + } +}); + +var upload = multer({storage: storage}); + +module.exports = upload; \ No newline at end of file diff --git a/app/controllers/file.controller.js b/app/controllers/file.controller.js new file mode 100644 index 0000000..dc98aed --- /dev/null +++ b/app/controllers/file.controller.js @@ -0,0 +1,17 @@ +const uploadFolder = __basedir + '/uploads/'; +const fs = require('fs'); + +exports.uploadFile = (req, res) => { + res.send('File uploaded successfully! -> filename = ' + req.file.filename); +} + +exports.listAllFiles = (req, res) => { + fs.readdir(uploadFolder, (err, files) => { + res.send(files); + }) +} + +exports.downloadFile = (req, res) => { + var filename = req.params.filename; + res.download(uploadFolder + filename); +} \ No newline at end of file diff --git a/app/routers/file.router.js b/app/routers/file.router.js new file mode 100644 index 0000000..ea4871f --- /dev/null +++ b/app/routers/file.router.js @@ -0,0 +1,26 @@ +module.exports = (app, router, upload) => { + const fileWorker = require('../controllers/file.controller.js'); + + var path = __basedir + '/views/'; + + router.use((req,res,next) => { + console.log("/" + req.method); + next(); + }); + + app.get('/', (req,res) => { + res.sendFile(path + "index.html"); + }); + + app.post('/api/files/upload', upload.single("uploadfile"), fileWorker.uploadFile); + + app.get('/api/files/getall', fileWorker.listAllFiles); + + app.get('/api/files/:filename', fileWorker.downloadFile); + + app.use('/',router); + + app.use('*', (req,res) => { + res.sendFile(path + "404.html"); + }); +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6788833 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,746 @@ +{ + "name": "nodejs-express-multer-multipartfile-ajax-jquery-bootstrap", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + }, + "@types/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==" + }, + "@types/cors": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz", + "integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==" + }, + "@types/node": { + "version": "14.14.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz", + "integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==" + }, + "JSONStream": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", + "requires": { + "jsonparse": "0.0.5", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "append-field": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", + "integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + }, + "docker-modem": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.3.7.tgz", + "integrity": "sha1-P1ENCfXTNNwhNCKPkr00RnEiffQ=", + "requires": { + "JSONStream": "0.10.0", + "debug": "^2.6.0", + "readable-stream": "~1.0.26-4", + "split-ca": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "engine.io": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.5.tgz", + "integrity": "sha512-Ri+whTNr2PKklxQkfbGjwEo+kCBUM4Qxk4wtLqLrhH+b1up2NFL9g9pjYWiCV/oazwB0rArnvF/ZmZN2ab5Hpg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.1.0", + "engine.io-parser": "~4.0.0", + "ws": "^7.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz", + "integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=", + "requires": { + "append-field": "^0.1.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.0", + "mkdirp": "^0.5.1", + "object-assign": "^3.0.0", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-docker-api": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/node-docker-api/-/node-docker-api-1.1.22.tgz", + "integrity": "sha1-IwMn79MJpuxzAr8/QEq3YAQKWAY=", + "requires": { + "docker-modem": "^0.3.1", + "memorystream": "^0.3.1" + } + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "proxy-addr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.6.0" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "socket.io": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.4.tgz", + "integrity": "sha512-Vj1jUoO75WGc9txWd311ZJJqS9Dr8QtNJJ7gk2r7dcM/yGe9sit7qOijQl3GAwhpBOz/W8CwkD7R6yob07nLbA==", + "requires": { + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.8", + "@types/node": "^14.14.7", + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.1.0", + "engine.io": "~4.0.0", + "socket.io-adapter": "~2.0.3", + "socket.io-parser": "~4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "socket.io-adapter": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz", + "integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ==" + }, + "socket.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz", + "integrity": "sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.1.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "ws": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", + "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..090c2fb --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "nodejs-express-multer-multipartfile-ajax-jquery-bootstrap", + "version": "1.0.0", + "description": "Building a NodeJS/Express web-application that uses Multer middleware to upload MultipartFile with Ajax-JQuery & Bootstrap view", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "NodeJS", + "Express", + "Multer", + "Bootstrap", + "Ajax-Jquery" + ], + "author": "JavaSampleApproach.com", + "license": "ISC", + "dependencies": { + "express": "^4.16.3", + "multer": "^1.3.0", + "node-docker-api": "^1.1.22", + "socket.io": "^3.0.4" + } +} diff --git a/resources/static/css/style.css b/resources/static/css/style.css new file mode 100644 index 0000000..2fa428c --- /dev/null +++ b/resources/static/css/style.css @@ -0,0 +1,129 @@ +body { + overflow-x: hidden; + font-size: 18px; + position: relative; + min-height: 100vh; +} + +html, body { + height: 100%; + width: 100%; + background-color: #FFF; +} + +body { + position: relative; + min-height: 100vh; +} + +body::after { + content: ''; + display: block; + height: 220px; + /* Set same as footer's height */ +} + +/*-------- Navbar --------*/ + +.navbar .nav-link { + font-size: 20px; + background-color: #04021c; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +.bg-dark { + background-color: #04021c!important; +} + +.navbar-nav > li > a:hover { + transition: all 0.3s; + color: #FFF; +} + +.navbar .active { + background: #FFF !important; + color: #000 !important; +} + +.navbar-nav > li > a:hover { + cursor:pointer; + background-color: #FFF; + color: #000 !important; +} + +#console-box { + height: 50%; +} + +#console { + list-style: none; + -webkit-padding-start: 0; + -webkit-margin-after: 0; + -webkit-margin-before: 1em; + border: 1px solid #000; + overflow-y: auto; + width: 100%; + background: #333; + color: #fff; + height: 200px; + max-height: 200px; +} + +.box-shadow { + box-shadow: 0px 10px 50px rgba(0, 0, 0, 0.6); +} + +.subfooter { + position: absolute; + bottom: 60px; + width: 100%; + height: 160px; + background-color: #04021c/*#2d3436*/; + color: white; +} + +#sub-footer li { + font-size: .8125rem; + font-weight: 300; +} + +#sub-footer a { + color: #15abe2; +} + +#sub-footer a:visited { + border-bottom: #D7D7D7; +} + +#sub-footer a, a:link { + transition: all .4s; +} + +.exeter { + margin-top: 10%; + margin-left: 40%; + width: 80%; + content:url("/static/img/exeter.png"); +} + +.russell { + margin-top: 5%; + margin-left: 40%; + width: 80%; + content:url("/static/img/russell.png"); +} + +#main-footer { + background: #000; + color: #636e72; +} + +.footer { + position: absolute; + bottom: 0; + width: 100%; + height: 60px; + line-height: 60px; + background-color: #f5f5f5; +} diff --git a/resources/static/download-icon.png b/resources/static/download-icon.png new file mode 100644 index 0000000..9002b47 Binary files /dev/null and b/resources/static/download-icon.png differ diff --git a/resources/static/graph-creator.css b/resources/static/graph-creator.css new file mode 100644 index 0000000..c5fa7ce --- /dev/null +++ b/resources/static/graph-creator.css @@ -0,0 +1,98 @@ +body{ + margin: 0; + padding: 0; + overflow:hidden; +} + +p{ + text-align: center; + overflow: overlay; + position: relative; +} + +body{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: rgb(248, 248, 248) +} + +#toolbox{ + position: absolute; + bottom: 0; + left: 0; + margin-bottom: 0.5em; + margin-left: 1em; + border: 2px solid #EEEEEE; + border-radius: 5px; + padding: 1em; + z-index: 5; +} + +#toolbox input{ + width: 30px; + opacity: 0.4; +} +#toolbox input:hover{ + opacity: 1; + cursor: pointer; +} + +#hidden-file-upload{ + display: none; +} + +#download-input{ + margin: 0 0.5em; +} + +.conceptG text{ + pointer-events: none; +} + +marker{ + fill: #333; +} + +g.conceptG circle{ + fill: #F6FBFF; + stroke: #333; + stroke-width: 2px; +} + +g.conceptG:hover circle{ + fill: rgb(200, 238, 241); +} + +g.selected circle{ + fill: rgb(250, 232, 255); +} +g.selected:hover circle{ + fill: rgb(250, 232, 255); +} + +path.link { + fill: none; + stroke: #333; + stroke-width: 6px; + cursor: default; +} + +path.link:hover{ + stroke: rgb(94, 196, 204); +} + +g.connect-node circle{ + fill: #BEFFFF; +} + +path.link.hidden{ + stroke-width: 0; +} + +path.link.selected { + stroke: rgb(229, 172, 247); +} diff --git a/resources/static/graph-creator.js b/resources/static/graph-creator.js new file mode 100644 index 0000000..ca6050f --- /dev/null +++ b/resources/static/graph-creator.js @@ -0,0 +1,648 @@ +var docEl = document.documentElement, + bodyEl = document.getElementsByTagName('body')[0]; + +var width = window.innerWidth || docEl.clientWidth || bodyEl.clientWidth, + height = window.innerHeight || docEl.clientHeight || bodyEl.clientHeight; + +function fitscreen(nodes, width, height) { + var minX = nodes[0].x; + var maxX = nodes[0].x; + var minY = nodes[0].y; + var maxY = nodes[0].y; + var offsetX = 0, offsetY = 0; + + nodes.forEach(function (e, i) { + if (e.x > maxX) { + maxX = e.x + } + if (e.x < minX) { + minX = e.x + } + if (e.y < minY) { + minY = e.y + } + if (e.y < minY) { + minY = e.y + } + }); + + if (minX < 0) { + offsetX = - minX; + minX += offsetX; + maxX += offsetX; + } + + if (minY < 0) { + offsetY = -minY; + minY += offsetY + maxY += offsetY; + } + + nodes.forEach(function (e, i) { + e.x += offsetX; + e.y += offsetY; + e.x = (e.x - minX) * width * 0.8 / (maxX - minX) + 0.25 * width; + e.y = (e.y - minY) * height * 0.8 / (maxY - minY) + 0.25 * height; + }); + + return nodes; +} + +document.onload = (function (d3, saveAs, Blob, undefined) { + // define graphcreator object + var GraphCreator = function (svg, nodes, edges) { + var thisGraph = this; + thisGraph.idct = 0; + + thisGraph.nodes = nodes || []; + thisGraph.edges = edges || []; + + thisGraph.state = { + selectedNode: null, + selectedEdge: null, + mouseDownNode: null, + mouseDownLink: null, + justDragged: false, + justScaleTransGraph: false, + lastKeyDown: -1, + shiftNodeDrag: false, + selectedText: null + }; + + // define arrow markers for graph links + var defs = svg.append('svg:defs'); + defs.append('svg:marker') + .attr('id', 'end-arrow') + .attr('viewBox', '0 -5 10 10') + .attr('refX', "20") + .attr('markerWidth', 3.5) + .attr('markerHeight', 3.5) + .attr('orient', 'auto') + .append('svg:path') + .attr('d', 'M0,-5L10,0L0,5'); + + // define arrow markers for leading arrow + defs.append('svg:marker') + .attr('id', 'mark-end-arrow') + .attr('viewBox', '0 -5 10 10') + .attr('refX', 7) + .attr('markerWidth', 3.5) + .attr('markerHeight', 3.5) + .attr('orient', 'auto') + .append('svg:path') + .attr('d', 'M0,-5L10,0L0,5'); + + thisGraph.svg = svg; + thisGraph.svgG = svg.append("g") + .classed(thisGraph.consts.graphClass, true); + var svgG = thisGraph.svgG; + + // displayed when dragging between nodes + thisGraph.dragLine = svgG.append('svg:path') + .attr('class', 'link dragline hidden') + .attr('d', 'M0,0L0,0') + .style('marker-end', 'url(#mark-end-arrow)'); + + // svg nodes and edges + thisGraph.paths = svgG.append("g").selectAll("g"); + thisGraph.circles = svgG.append("g").selectAll("g"); + + thisGraph.drag = d3.behavior.drag() + .origin(function (d) { + return { x: d.x, y: d.y }; + }) + .on("drag", function (args) { + thisGraph.state.justDragged = true; + thisGraph.dragmove.call(thisGraph, args); + }) + .on("dragend", function () { + // todo check if edge-mode is selected + }); + + // listen for key events + d3.select(window).on("keydown", function () { + thisGraph.svgKeyDown.call(thisGraph); + }) + .on("keyup", function () { + thisGraph.svgKeyUp.call(thisGraph); + }); + svg.on("mousedown", function (d) { thisGraph.svgMouseDown.call(thisGraph, d); }); + svg.on("mouseup", function (d) { thisGraph.svgMouseUp.call(thisGraph, d); }); + + // listen for dragging + var dragSvg = d3.behavior.zoom() + .on("zoom", function () { + if (d3.event.sourceEvent.shiftKey) { + // TODO the internal d3 state is still changing + return false; + } else { + thisGraph.zoomed.call(thisGraph); + } + return true; + }) + .on("zoomstart", function () { + var ael = d3.select("#" + thisGraph.consts.activeEditId).node(); + if (ael) { + ael.blur(); + } + if (!d3.event.sourceEvent.shiftKey) d3.select('body').style("cursor", "move"); + }) + .on("zoomend", function () { + d3.select('body').style("cursor", "auto"); + }); + + svg.call(dragSvg).on("dblclick.zoom", null); + + // listen for resize + window.onresize = function () { thisGraph.updateWindow(svg); }; + + // handle download data + d3.select("#download-input").on("click", function () { + var saveEdges = []; + thisGraph.edges.forEach(function (val, i) { + saveEdges.push({ source: val.source.id, target: val.target.id }); + }); + var blob = new Blob([window.JSON.stringify({ "nodes": thisGraph.nodes, "edges": saveEdges })], { type: "text/plain;charset=utf-8" }); + saveAs(blob, "mydag.json"); + }); + + + // handle uploaded data + d3.select("#upload-input").on("click", function () { + document.getElementById("hidden-file-upload").click(); + }); + d3.select("#hidden-file-upload").on("change", function () { + if (window.File && window.FileReader && window.FileList && window.Blob) { + var uploadFile = this.files[0]; + var filereader = new window.FileReader(); + + filereader.onload = function () { + var txtRes = filereader.result; + // TODO better error handling + try { + var jsonObj = JSON.parse(txtRes); + jsonObj.nodes = fitscreen(jsonObj.nodes, width, height) + thisGraph.deleteGraph(true); + thisGraph.nodes = jsonObj.nodes; + thisGraph.setIdCt(jsonObj.nodes.length); + var newEdges = jsonObj.edges; + newEdges.forEach(function (e, i) { + newEdges[i] = { + source: thisGraph.nodes.filter(function (n) { return n.id == e.source; })[0], + target: thisGraph.nodes.filter(function (n) { return n.id == e.target; })[0] + }; + }); + thisGraph.edges = newEdges; + thisGraph.updateGraph(); + } catch (err) { + window.alert("Error parsing uploaded file\nerror message: " + err.message); + return; + } + }; + filereader.readAsText(uploadFile); + + } else { + alert("Your browser won't let you save this graph -- try upgrading your browser to IE 10+ or Chrome or Firefox."); + } + + }); + + // handle delete graph + d3.select("#delete-graph").on("click", function () { + thisGraph.deleteGraph(false); + }); + }; + + GraphCreator.prototype.setIdCt = function (idct) { + this.idct = idct; + }; + + GraphCreator.prototype.consts = { + selectedClass: "selected", + connectClass: "connect-node", + circleGClass: "conceptG", + graphClass: "graph", + activeEditId: "active-editing", + BACKSPACE_KEY: 8, + DELETE_KEY: 46, + ENTER_KEY: 13, + nodeRadius: 25 + }; + + /* PROTOTYPE FUNCTIONS */ + + GraphCreator.prototype.dragmove = function (d) { + var thisGraph = this; + if (thisGraph.state.shiftNodeDrag) { + thisGraph.dragLine.attr('d', 'M' + d.x + ',' + d.y + 'L' + d3.mouse(thisGraph.svgG.node())[0] + ',' + d3.mouse(this.svgG.node())[1]); + } else { + d.x += d3.event.dx; + d.y += d3.event.dy; + thisGraph.updateGraph(); + } + }; + + GraphCreator.prototype.deleteGraph = function (skipPrompt) { + var thisGraph = this, + doDelete = true; + if (!skipPrompt) { + doDelete = window.confirm("Press OK to delete this graph"); + } + if (doDelete) { + thisGraph.nodes = []; + thisGraph.edges = []; + thisGraph.updateGraph(); + } + }; + + /* select all text in element: taken from http://stackoverflow.com/questions/6139107/programatically-select-text-in-a-contenteditable-html-element */ + GraphCreator.prototype.selectElementContents = function (el) { + var range = document.createRange(); + range.selectNodeContents(el); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + }; + + + /* insert svg line breaks: taken from http://stackoverflow.com/questions/13241475/how-do-i-include-newlines-in-labels-in-d3-charts */ + GraphCreator.prototype.insertTitleLinebreaks = function (gEl, title) { + var words = title.split(/\s+/g), + nwords = words.length; + var el = gEl.append("text") + .attr("text-anchor", "middle") + .attr("dy", "-" + (nwords - 1) * 7.5); + + for (var i = 0; i < words.length; i++) { + var tspan = el.append('tspan').text(words[i]); + if (i > 0) + tspan.attr('x', 0).attr('dy', '15'); + } + }; + + + // remove edges associated with a node + GraphCreator.prototype.spliceLinksForNode = function (node) { + var thisGraph = this, + toSplice = thisGraph.edges.filter(function (l) { + return (l.source === node || l.target === node); + }); + toSplice.map(function (l) { + thisGraph.edges.splice(thisGraph.edges.indexOf(l), 1); + }); + }; + + GraphCreator.prototype.replaceSelectEdge = function (d3Path, edgeData) { + var thisGraph = this; + d3Path.classed(thisGraph.consts.selectedClass, true); + if (thisGraph.state.selectedEdge) { + thisGraph.removeSelectFromEdge(); + } + thisGraph.state.selectedEdge = edgeData; + }; + + GraphCreator.prototype.replaceSelectNode = function (d3Node, nodeData) { + var thisGraph = this; + d3Node.classed(this.consts.selectedClass, true); + if (thisGraph.state.selectedNode) { + thisGraph.removeSelectFromNode(); + } + thisGraph.state.selectedNode = nodeData; + }; + + GraphCreator.prototype.removeSelectFromNode = function () { + var thisGraph = this; + thisGraph.circles.filter(function (cd) { + return cd.id === thisGraph.state.selectedNode.id; + }).classed(thisGraph.consts.selectedClass, false); + thisGraph.state.selectedNode = null; + }; + + GraphCreator.prototype.removeSelectFromEdge = function () { + var thisGraph = this; + thisGraph.paths.filter(function (cd) { + return cd === thisGraph.state.selectedEdge; + }).classed(thisGraph.consts.selectedClass, false); + thisGraph.state.selectedEdge = null; + }; + + GraphCreator.prototype.pathMouseDown = function (d3path, d) { + var thisGraph = this, + state = thisGraph.state; + d3.event.stopPropagation(); + state.mouseDownLink = d; + + if (state.selectedNode) { + thisGraph.removeSelectFromNode(); + } + + var prevEdge = state.selectedEdge; + if (!prevEdge || prevEdge !== d) { + thisGraph.replaceSelectEdge(d3path, d); + } else { + thisGraph.removeSelectFromEdge(); + } + }; + + // mousedown on node + GraphCreator.prototype.circleMouseDown = function (d3node, d) { + var thisGraph = this, + state = thisGraph.state; + d3.event.stopPropagation(); + state.mouseDownNode = d; + if (d3.event.shiftKey) { + state.shiftNodeDrag = d3.event.shiftKey; + // reposition dragged directed edge + thisGraph.dragLine.classed('hidden', false) + .attr('d', 'M' + d.x + ',' + d.y + 'L' + d.x + ',' + d.y); + return; + } + }; + + /* place editable text on node in place of svg text */ + GraphCreator.prototype.changeTextOfNode = function (d3node, d) { + var thisGraph = this, + consts = thisGraph.consts, + htmlEl = d3node.node(); + d3node.selectAll("text").remove(); + var nodeBCR = htmlEl.getBoundingClientRect(), + curScale = nodeBCR.width / consts.nodeRadius, + placePad = 5 * curScale, + useHW = curScale > 1 ? nodeBCR.width * 0.71 : consts.nodeRadius * 1.42; + // replace with editableconent text + var d3txt = thisGraph.svg.selectAll("foreignObject") + .data([d]) + .enter() + .append("foreignObject") + .attr("x", nodeBCR.left + placePad) + .attr("y", nodeBCR.top + placePad) + .attr("height", 2 * useHW) + .attr("width", useHW) + .append("xhtml:p") + .attr("id", consts.activeEditId) + .attr("contentEditable", "true") + .text(d.title) + .on("mousedown", function (d) { + d3.event.stopPropagation(); + }) + .on("keydown", function (d) { + d3.event.stopPropagation(); + if (d3.event.keyCode == consts.ENTER_KEY && !d3.event.shiftKey) { + this.blur(); + } + }) + .on("blur", function (d) { + d.title = this.textContent; + thisGraph.insertTitleLinebreaks(d3node, d.title); + d3.select(this.parentElement).remove(); + }); + return d3txt; + }; + + // mouseup on nodes + GraphCreator.prototype.circleMouseUp = function (d3node, d) { + var thisGraph = this, + state = thisGraph.state, + consts = thisGraph.consts; + // reset the states + state.shiftNodeDrag = false; + d3node.classed(consts.connectClass, false); + + var mouseDownNode = state.mouseDownNode; + + if (!mouseDownNode) return; + + thisGraph.dragLine.classed("hidden", true); + + if (mouseDownNode !== d) { + // we're in a different node: create new edge for mousedown edge and add to graph + var newEdge = { source: mouseDownNode, target: d }; + var filtRes = thisGraph.paths.filter(function (d) { + if (d.source === newEdge.target && d.target === newEdge.source) { + thisGraph.edges.splice(thisGraph.edges.indexOf(d), 1); + } + return d.source === newEdge.source && d.target === newEdge.target; + }); + if (!filtRes[0].length) { + thisGraph.edges.push(newEdge); + thisGraph.updateGraph(); + } + } else { + // we're in the same node + if (state.justDragged) { + // dragged, not clicked + state.justDragged = false; + } else { + // clicked, not dragged + if (d3.event.shiftKey) { + // shift-clicked node: edit text content + var d3txt = thisGraph.changeTextOfNode(d3node, d); + var txtNode = d3txt.node(); + thisGraph.selectElementContents(txtNode); + txtNode.focus(); + } else { + if (state.selectedEdge) { + thisGraph.removeSelectFromEdge(); + } + var prevNode = state.selectedNode; + + if (!prevNode || prevNode.id !== d.id) { + thisGraph.replaceSelectNode(d3node, d); + } else { + thisGraph.removeSelectFromNode(); + } + } + } + } + state.mouseDownNode = null; + return; + + }; // end of circles mouseup + + // mousedown on main svg + GraphCreator.prototype.svgMouseDown = function () { + this.state.graphMouseDown = true; + }; + + // mouseup on main svg + GraphCreator.prototype.svgMouseUp = function () { + var thisGraph = this, + state = thisGraph.state; + if (state.justScaleTransGraph) { + // dragged not clicked + state.justScaleTransGraph = false; + } else if (state.graphMouseDown && d3.event.shiftKey) { + // clicked not dragged from svg + var xycoords = d3.mouse(thisGraph.svgG.node()), + d = { id: thisGraph.idct, title: (this.idct).toString(), x: xycoords[0], y: xycoords[1] }; + thisGraph.idct++; + thisGraph.nodes.push(d); + thisGraph.updateGraph(); + // make title of text immediently editable + var d3txt = thisGraph.changeTextOfNode(thisGraph.circles.filter(function (dval) { + return dval.id === d.id; + }), d), + txtNode = d3txt.node(); + thisGraph.selectElementContents(txtNode); + txtNode.focus(); + } else if (state.shiftNodeDrag) { + // dragged from node + state.shiftNodeDrag = false; + thisGraph.dragLine.classed("hidden", true); + } + state.graphMouseDown = false; + }; + + // keydown on main svg + GraphCreator.prototype.svgKeyDown = function () { + var thisGraph = this, + state = thisGraph.state, + consts = thisGraph.consts; + // make sure repeated key presses don't register for each keydown + if (state.lastKeyDown !== -1) return; + + state.lastKeyDown = d3.event.keyCode; + var selectedNode = state.selectedNode, + selectedEdge = state.selectedEdge; + + switch (d3.event.keyCode) { + case consts.BACKSPACE_KEY: + case consts.DELETE_KEY: + d3.event.preventDefault(); + if (selectedNode) { + thisGraph.nodes.splice(thisGraph.nodes.indexOf(selectedNode), 1); + thisGraph.spliceLinksForNode(selectedNode); + state.selectedNode = null; + thisGraph.updateGraph(); + } else if (selectedEdge) { + thisGraph.edges.splice(thisGraph.edges.indexOf(selectedEdge), 1); + state.selectedEdge = null; + thisGraph.updateGraph(); + } + break; + } + }; + + GraphCreator.prototype.svgKeyUp = function () { + this.state.lastKeyDown = -1; + }; + + // call to propagate changes to graph + GraphCreator.prototype.updateGraph = function () { + + var thisGraph = this, + consts = thisGraph.consts, + state = thisGraph.state; + + thisGraph.paths = thisGraph.paths.data(thisGraph.edges, function (d) { + return String(d.source.id) + "+" + String(d.target.id); + }); + var paths = thisGraph.paths; + // update existing paths + paths.style('marker-end', 'url(#end-arrow)') + .classed(consts.selectedClass, function (d) { + return d === state.selectedEdge; + }) + .attr("d", function (d) { + return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y; + }); + + // add new paths + paths.enter() + .append("path") + .style('marker-end', 'url(#end-arrow)') + .classed("link", true) + .attr("d", function (d) { + return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y; + }) + .on("mousedown", function (d) { + thisGraph.pathMouseDown.call(thisGraph, d3.select(this), d); + } + ) + .on("mouseup", function (d) { + state.mouseDownLink = null; + }); + + // remove old links + paths.exit().remove(); + + // update existing nodes + thisGraph.circles = thisGraph.circles.data(thisGraph.nodes, function (d) { return d.id; }); + thisGraph.circles.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; }); + + // add new nodes + var newGs = thisGraph.circles.enter() + .append("g"); + + newGs.classed(consts.circleGClass, true) + .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; }) + .on("mouseover", function (d) { + if (state.shiftNodeDrag) { + d3.select(this).classed(consts.connectClass, true); + } + }) + .on("mouseout", function (d) { + d3.select(this).classed(consts.connectClass, false); + }) + .on("mousedown", function (d) { + thisGraph.circleMouseDown.call(thisGraph, d3.select(this), d); + }) + .on("mouseup", function (d) { + thisGraph.circleMouseUp.call(thisGraph, d3.select(this), d); + }) + .call(thisGraph.drag); + + newGs.append("circle") + .attr("r", String(consts.nodeRadius)); + + newGs.each(function (d) { + thisGraph.insertTitleLinebreaks(d3.select(this), d.title); + }); + + // remove old nodes + thisGraph.circles.exit().remove(); + }; + + GraphCreator.prototype.zoomed = function () { + this.state.justScaleTransGraph = true; + d3.select("." + this.consts.graphClass) + .attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")"); + }; + + GraphCreator.prototype.updateWindow = function (svg) { + var docEl = document.documentElement, + bodyEl = document.getElementsByTagName('body')[0]; + var x = window.innerWidth || docEl.clientWidth || bodyEl.clientWidth; + var y = window.innerHeight || docEl.clientHeight || bodyEl.clientHeight; + svg.attr("width", x).attr("height", y); + }; + + /**** MAIN ****/ + + // warn the user when leaving + // window.onbeforeunload = function(){ + // return "Make sure to save your graph locally before leaving"; + // }; + + // initial node data + var obj = JSON.parse('{"nodes": [{"title": "0", "id": 0, "x": 38, "y": 20}, {"title": "1", "id": 1, "x": 39, "y": 26}, {"title": "2", "id": 2, "x": 40, "y": 25}, {"title": "3", "id": 3, "x": 36, "y": 23}, {"title": "4", "id": 4, "x": 33, "y": 10}, {"title": "5", "id": 5, "x": 37, "y": 12}, {"title": "6", "id": 6, "x": 38, "y": 13}, {"title": "7", "id": 7, "x": 37, "y": 20}, {"title": "8", "id": 8, "x": 41, "y": 9}, {"title": "9", "id": 9, "x": 41, "y": 13}, {"title": "10", "id": 10, "x": 36, "y": -5}, {"title": "11", "id": 11, "x": 38, "y": 15}, {"title": "12", "id": 12, "x": 38, "y": 15}, {"title": "13", "id": 13, "x": 37, "y": 15}, {"title": "14", "id": 14, "x": 35, "y": 14}, {"title": "15", "id": 15, "x": 39, "y": 19}], "edges": [{"source": 8, "target": 10}, {"source": 10, "target": 4}, {"source": 4, "target": 14}, {"source": 14, "target": 0}, {"source": 0, "target": 7}, {"source": 7, "target": 3}, {"source": 3, "target": 1}, {"source": 1, "target": 2}, {"source": 2, "target": 15}, {"source": 15, "target": 11}, {"source": 11, "target": 12}, {"source": 12, "target": 13}, {"source": 13, "target": 5}, {"source": 5, "target": 6}, {"source": 6, "target": 9}, {"source": 9, "target": 8}]}') + obj.nodes = fitscreen(obj.nodes, width, height); + + var newEdges = obj.edges; + newEdges.forEach(function (e, i) { + newEdges[i] = { + source: obj.nodes.filter(function (n) { return n.id == e.source; })[0], + target: obj.nodes.filter(function (n) { return n.id == e.target; })[0] + }; + }); + obj.edges = newEdges; + + /** MAIN SVG **/ + var svg = d3.select("body").append("svg") + .attr("width", width) + .attr("height", height); + var graph = new GraphCreator(svg, obj.nodes, obj.edges); + graph.setIdCt(obj.nodes.length); + graph.updateGraph(); + +})(window.d3, window.saveAs, window.Blob); diff --git a/resources/static/img/exeter.png b/resources/static/img/exeter.png new file mode 100644 index 0000000..b7380e9 Binary files /dev/null and b/resources/static/img/exeter.png differ diff --git a/resources/static/img/russell.png b/resources/static/img/russell.png new file mode 100644 index 0000000..432d8f5 Binary files /dev/null and b/resources/static/img/russell.png differ diff --git a/resources/static/index.html b/resources/static/index.html new file mode 100644 index 0000000..473424a --- /dev/null +++ b/resources/static/index.html @@ -0,0 +1,26 @@ + + + + + + + Document + + +
+

Hello

+
+ +
+
+
+
+
+

Leader

+ + +
+
+
+ + \ No newline at end of file diff --git a/resources/static/js/getrequest.js b/resources/static/js/getrequest.js new file mode 100644 index 0000000..8b7aac9 --- /dev/null +++ b/resources/static/js/getrequest.js @@ -0,0 +1,92 @@ +$( document ).ready( () => { + + $('body').scrollspy({ target: '#main-nav', offset: 130 }) + + $('#fullpage').fullpage({ + // anchors: ['underPage', 'gradPage', 'phdPage'], + sectionsColor: ['#ffffff', '#f8f8f8'], + autoScrolling: false, + css3: true, + fitToSection: false, + afterLoad: function(anchorLink, index) { + // history.pushState(null, null, ""); + // console.log(anchorLink); + } + }); + + $("a").on('click', function(event) { + // Make sure this.hash has a value before overriding default behavior + if (this.hash !== "") { + // Prevent default anchor click behavior + event.preventDefault(); + + // Store hash + var hash = this.hash; + + // Using jQuery's animate() method to add smooth page scroll + // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area + var offset = 0; + if (hash === "#home") + { + offset = -100; + } + else { + offset = -60; + } + $('html, body').animate({ + scrollTop: ($(hash).offset().top + offset) + }, 1000, function(){ + // Add hash (#) to URL when done scrolling (default click behavior) + // window.location.hash = hash; + }); + } + }); + + // initialize with defaults + // $("#uploadfile").fileinput(); + + // with plugin options + $("#uploadfile").fileinput({ + // theme: "fa", + 'theme': 'fas', + showUpload:false, + previewFileType:'py', + maxFileCount: 1, + allowedFileExtensions: ["py"] + }); + + ConsoleLogHTML.connect(document.getElementById("console")); // Redirect log messages + // ConsoleLogHTML.disconnect(); // Stop redirecting + + var url = window.location; + + // GET REQUEST + $("#btnGetFiles").click( (event) => { + event.preventDefault(); + ajaxGet(); + }); + + // DO GET + function ajaxGet(){ + $.ajax({ + type : "GET", + url : "/api/files/getall", + success: (data) => { + //clear old data + $("#listFiles").html(""); + + /* + render list of files + */ + $("#listFiles").append(''); + }, + error : (err) => { + $("#listFiles").html(err.responseText); + } + }); + } +}) \ No newline at end of file diff --git a/resources/static/js/postrequest.js b/resources/static/js/postrequest.js new file mode 100644 index 0000000..bf9a068 --- /dev/null +++ b/resources/static/js/postrequest.js @@ -0,0 +1,81 @@ +// warn the user when leaving +window.onbeforeunload = function(){ + return "Make sure to save your graph locally before leaving"; +}; + +var socket = io(); + +socket.on('connect', () => { + $("#session_id").text(socket.id); + window.id = socket.id + console.log('Session Id: ', socket.id); // an alphanumeric id... +}); + +socket.on('users_count', (clients) => { + $("#user_counts").text(clients); +}); + +socket.on('start', () => { + console.log('Building start'); +}); + +$(document).ready( () => { + $("#btnSubmit").click((event) => { + //stop submit the form, we will post it manually. + event.preventDefault(); + doAjax(); + }); + + $('#uploadfile').change(function(e){ + if(e.target.files[0]) + { + var fileName = e.target.files[0].name; + if(fileName !== "my_model.py") + { + alert('Please upload my_model.py'); + $('#btnSubmit').prop('disabled', true); + } + else + { + $('#btnSubmit').prop('disabled', false); + } + } + }); +}); + +function doAjax() { + + // Get form + var form = $('#fileUploadForm')[0]; + + var data = new FormData(form); + var file = data.get('uploadfile'); + + var renameFile =new File([file], window.id + '.py' ,{type:file.type}); + var formdata = new FormData(); + formdata.append('uploadfile', renameFile); + // console.log(formdata.get('uploadfile')) + if(window.id) { + $.ajax({ + type: "POST", + enctype: 'multipart/form-data', + url: "/api/files/upload", + data: formdata, + processData: false, //prevent jQuery from automatically transforming the data into a query string + contentType: false, + cache: false, + success: (data) => { + // $("#listFiles").text(data); + console.log('Emit build request') + socket.emit('build', {}); + }, + error: (e) => { + $("#listFiles").text(e.responseText); + } + }); + } + else + { + alert('Failed to connect to server'); + } +} \ No newline at end of file diff --git a/resources/static/preview.html b/resources/static/preview.html new file mode 100644 index 0000000..b9938e7 --- /dev/null +++ b/resources/static/preview.html @@ -0,0 +1,21 @@ + + + + + + + + + +
+
+   + + +
+
+ + + + + \ No newline at end of file diff --git a/resources/static/themes/explorer-fas/theme.css b/resources/static/themes/explorer-fas/theme.css new file mode 100644 index 0000000..9cb976a --- /dev/null +++ b/resources/static/themes/explorer-fas/theme.css @@ -0,0 +1,156 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Krajee Explorer Font Awesome 5.x theme style for bootstrap-fileinput. Load this theme file after loading + * font awesome 5.x CSS and `fileinput.css`. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +.theme-explorer-fas .file-preview-frame { + border: 1px solid #ddd; + margin: 2px 0; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; +} +.theme-explorer-fas .file-actions, +.theme-explorer-fas .file-upload-indicator, .theme-explorer-fas .file-drag-handle, .theme-explorer-fas .explorer-frame .kv-file-content, .theme-explorer-fas .file-actions, .explorer-frame .file-preview-other { + text-align: center; +} +.theme-explorer-fas .file-upload-indicator, .theme-explorer-fas .file-drag-handle { + position: absolute; + display: inline-block; + bottom: 8px; + right: 4px; + width: 16px; + height: 16px; + font-size: 16px; +} +.theme-explorer-fas .file-thumb-progress .progress, .theme-explorer-fas .explorer-caption { + display: block; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +.theme-explorer-fas .file-thumb-progress .progress { + margin-top: 5px; +} +.theme-explorer-fas .explorer-caption, +.theme-explorer-fas .file-footer-buttons { + padding: 5px; +} +.theme-explorer-fas .file-footer-buttons { + text-align: right; +} +.theme-explorer-fas .explorer-caption { + color: #777; + padding-top: 5px; +} +.theme-explorer-fas .kvsortable-ghost { + opacity: 0.6; + background: #e1edf7; + border: 2px solid #a1abff; +} +.theme-explorer-fas .file-preview .table { + margin: 0; +} +.theme-explorer-fas .file-error-message ul { + padding: 5px 0 0 20px; +} +.explorer-frame .file-preview-text { + display: inline-block; + color: #428bca; + border: 1px solid #ddd; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + outline: none; + padding: 8px; + resize: none; +} +.explorer-frame .file-preview-html { + display: inline-block; + border: 1px solid #ddd; + padding: 8px; + overflow: auto; +} +.explorer-frame .file-other-icon { + font-size: 2.6em; +} +.explorer-frame:hover { + background-color: #f5f5f5; +} +.theme-explorer-fas .file-preview-frame samp { + font-size: 0.9rem; +} +.theme-explorer-fas .explorer-frame .kv-file-content { + width: 160px; + height: 80px; + padding: 5px; + text-align: left; +} +.theme-explorer-fas .file-details-cell { + width: 60%; + font-size: 0.95rem; + text-align: left; + margin-right: auto; +} +.theme-explorer-fas .file-actions-cell { + position: relative; + height: 80px; + width: 200px; +} +/*noinspection CssOverwrittenProperties*/ +.file-zoom-dialog .explorer-frame .file-other-icon { + font-size: 22em; + font-size: 50vmin; +} +@media only screen and (max-width: 1249px) { + .theme-explorer-fas .file-preview-frame .file-details-cell { + width: 40%; + } +} +@media only screen and (max-width: 1023px) { + .theme-explorer-fas .file-preview-frame .file-details-cell { + width: 30%; + } +} +@media only screen and (max-width: 767px) { + .theme-explorer-fas .file-preview-frame .file-details-cell { + width: 200px; + } +} +@media only screen and (max-width: 575px) { + .theme-explorer-fas .file-preview-frame { + flex-direction: column; + } + + .theme-explorer-fas .file-preview-frame .kv-file-content { + width: auto; + text-align: center; + } + + .theme-explorer-fas .file-details-cell { + width: 100px; + text-align: center; + margin-right: 0; + } + + .theme-explorer-fas .file-preview-frame .kv-file-content, + .theme-explorer-fas .file-details-cell, + .theme-explorer-fas .file-actions-cell { + width: 100%; + } + + .theme-explorer-fas .file-actions-cell { + height: auto; + } + + .theme-explorer-fas .file-footer-buttons { + text-align: left; + } +} \ No newline at end of file diff --git a/resources/static/themes/explorer-fas/theme.js b/resources/static/themes/explorer-fas/theme.js new file mode 100644 index 0000000..589321a --- /dev/null +++ b/resources/static/themes/explorer-fas/theme.js @@ -0,0 +1,71 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Krajee Explorer Font Awesome theme configuration for bootstrap-fileinput. + * Load this theme file after loading `fileinput.js`. Ensure that + * font awesome assets and CSS are loaded on the page as well. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +(function ($) { + 'use strict'; + $.fn.fileinputThemes['explorer-fas'] = { + layoutTemplates: { + footer: '
' + + '
{caption}
' + '{size}{progress}' + + '
' + + '
{indicator} {actions}
', + actions: '{drag}\n' + + '
\n' + + ' \n' + + '
', + fileIcon: ' ' + }, + previewSettings: { + html: {width: '100px', height: '60px'}, + text: {width: '100px', height: '60px'}, + video: {width: 'auto', height: '60px'}, + audio: {width: 'auto', height: '60px'}, + flash: {width: '100%', height: '60px'}, + object: {width: '100%', height: '60px'}, + pdf: {width: '100px', height: '60px'}, + other: {width: '100%', height: '60px'} + }, + frameClass: 'explorer-frame', + fileActionSettings: { + removeIcon: '', + uploadIcon: '', + uploadRetryIcon: '', + downloadIcon: '', + zoomIcon: '', + dragIcon: '', + indicatorNew: '', + indicatorSuccess: '', + indicatorError: '', + indicatorLoading: '', + indicatorPaused: '' + }, + previewZoomButtonIcons: { + prev: '', + next: '', + toggleheader: '', + fullscreen: '', + borderless: '', + close: '' + }, + previewFileIcon: '', + browseIcon: '', + removeIcon: '', + cancelIcon: '', + pauseIcon: '', + uploadIcon: '', + msgValidationErrorIcon: ' ' + }; +})(window.jQuery); \ No newline at end of file diff --git a/resources/static/themes/explorer-fas/theme.min.css b/resources/static/themes/explorer-fas/theme.min.css new file mode 100644 index 0000000..f9273d7 --- /dev/null +++ b/resources/static/themes/explorer-fas/theme.min.css @@ -0,0 +1,13 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Krajee Explorer Font Awesome 5.x theme style for bootstrap-fileinput. Load this theme file after loading + * font awesome 5.x CSS and `fileinput.css`. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */.theme-explorer-fas .file-preview-frame{border:1px solid #ddd;margin:2px 0;width:100%;display:flex;justify-content:space-between;align-items:center}.explorer-frame .file-preview-other,.theme-explorer-fas .explorer-frame .kv-file-content,.theme-explorer-fas .file-actions,.theme-explorer-fas .file-drag-handle,.theme-explorer-fas .file-upload-indicator{text-align:center}.theme-explorer-fas .file-drag-handle,.theme-explorer-fas .file-upload-indicator{position:absolute;display:inline-block;bottom:8px;right:4px;width:16px;height:16px;font-size:16px}.theme-explorer-fas .explorer-caption,.theme-explorer-fas .file-thumb-progress .progress{display:block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.theme-explorer-fas .file-thumb-progress .progress{margin-top:5px}.theme-explorer-fas .explorer-caption,.theme-explorer-fas .file-footer-buttons{padding:5px}.theme-explorer-fas .file-footer-buttons{text-align:right}.theme-explorer-fas .explorer-caption{color:#777;padding-top:5px}.theme-explorer-fas .kvsortable-ghost{opacity:.6;background:#e1edf7;border:2px solid #a1abff}.theme-explorer-fas .file-preview .table{margin:0}.theme-explorer-fas .file-error-message ul{padding:5px 0 0 20px}.explorer-frame .file-preview-text{display:inline-block;color:#428bca;border:1px solid #ddd;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;outline:0;padding:8px;resize:none}.explorer-frame .file-preview-html{display:inline-block;border:1px solid #ddd;padding:8px;overflow:auto}.explorer-frame .file-other-icon{font-size:2.6em}.explorer-frame:hover{background-color:#f5f5f5}.theme-explorer-fas .file-preview-frame samp{font-size:.9rem}.theme-explorer-fas .explorer-frame .kv-file-content{width:160px;height:80px;padding:5px;text-align:left}.theme-explorer-fas .file-details-cell{width:60%;font-size:.95rem;text-align:left;margin-right:auto}.theme-explorer-fas .file-actions-cell{position:relative;height:80px;width:200px}.file-zoom-dialog .explorer-frame .file-other-icon{font-size:22em;font-size:50vmin}@media only screen and (max-width:1249px){.theme-explorer-fas .file-preview-frame .file-details-cell{width:40%}}@media only screen and (max-width:1023px){.theme-explorer-fas .file-preview-frame .file-details-cell{width:30%}}@media only screen and (max-width:767px){.theme-explorer-fas .file-preview-frame .file-details-cell{width:200px}}@media only screen and (max-width:575px){.theme-explorer-fas .file-preview-frame{flex-direction:column}.theme-explorer-fas .file-preview-frame .kv-file-content{text-align:center}.theme-explorer-fas .file-details-cell{text-align:center;margin-right:0}.theme-explorer-fas .file-actions-cell,.theme-explorer-fas .file-details-cell,.theme-explorer-fas .file-preview-frame .kv-file-content{width:100%}.theme-explorer-fas .file-actions-cell{height:auto}.theme-explorer-fas .file-footer-buttons{text-align:left}} \ No newline at end of file diff --git a/resources/static/themes/explorer-fas/theme.min.js b/resources/static/themes/explorer-fas/theme.min.js new file mode 100644 index 0000000..6481b9e --- /dev/null +++ b/resources/static/themes/explorer-fas/theme.min.js @@ -0,0 +1,14 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Krajee Explorer Font Awesome theme configuration for bootstrap-fileinput. + * Load this theme file after loading `fileinput.js`. Ensure that + * font awesome assets and CSS are loaded on the page as well. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */!function(a){"use strict";a.fn.fileinputThemes["explorer-fas"]={layoutTemplates:{footer:'
{caption}
{size}{progress}
{indicator} {actions}
',actions:'{drag}\n
\n \n
',fileIcon:' '},previewSettings:{html:{width:"100px",height:"60px"},text:{width:"100px",height:"60px"},video:{width:"auto",height:"60px"},audio:{width:"auto",height:"60px"},flash:{width:"100%",height:"60px"},object:{width:"100%",height:"60px"},pdf:{width:"100px",height:"60px"},other:{width:"100%",height:"60px"}},frameClass:"explorer-frame",fileActionSettings:{removeIcon:'',uploadIcon:'',uploadRetryIcon:'',downloadIcon:'',zoomIcon:'',dragIcon:'',indicatorNew:'',indicatorSuccess:'',indicatorError:'',indicatorLoading:'',indicatorPaused:''},previewZoomButtonIcons:{prev:'',next:'',toggleheader:'',fullscreen:'',borderless:'',close:''},previewFileIcon:'',browseIcon:'',removeIcon:'',cancelIcon:'',pauseIcon:'',uploadIcon:'',msgValidationErrorIcon:' '}}(window.jQuery); \ No newline at end of file diff --git a/resources/static/themes/fas/theme.js b/resources/static/themes/fas/theme.js new file mode 100644 index 0000000..a53a90c --- /dev/null +++ b/resources/static/themes/fas/theme.js @@ -0,0 +1,49 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Font Awesome 5 icon theme configuration for bootstrap-fileinput. Requires font awesome 5 assets to be loaded. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +(function ($) { + "use strict"; + + $.fn.fileinputThemes.fas = { + fileActionSettings: { + removeIcon: '', + uploadIcon: '', + uploadRetryIcon: '', + downloadIcon: '', + zoomIcon: '', + dragIcon: '', + indicatorNew: '', + indicatorSuccess: '', + indicatorError: '', + indicatorLoading: '', + indicatorPaused: '' + }, + layoutTemplates: { + fileIcon: ' ' + }, + previewZoomButtonIcons: { + prev: '', + next: '', + toggleheader: '', + fullscreen: '', + borderless: '', + close: '' + }, + previewFileIcon: '', + browseIcon: '', + removeIcon: '', + cancelIcon: '', + pauseIcon: '', + uploadIcon: '', + msgValidationErrorIcon: ' ' + }; +})(window.jQuery); diff --git a/resources/static/themes/fas/theme.min.js b/resources/static/themes/fas/theme.min.js new file mode 100644 index 0000000..87c55fe --- /dev/null +++ b/resources/static/themes/fas/theme.min.js @@ -0,0 +1,12 @@ +/*! + * bootstrap-fileinput v5.1.3 + * http://plugins.krajee.com/file-input + * + * Font Awesome 5 icon theme configuration for bootstrap-fileinput. Requires font awesome 5 assets to be loaded. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2020, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */!function(a){"use strict";a.fn.fileinputThemes.fas={fileActionSettings:{removeIcon:'',uploadIcon:'',uploadRetryIcon:'',downloadIcon:'',zoomIcon:'',dragIcon:'',indicatorNew:'',indicatorSuccess:'',indicatorError:'',indicatorLoading:'',indicatorPaused:''},layoutTemplates:{fileIcon:' '},previewZoomButtonIcons:{prev:'',next:'',toggleheader:'',fullscreen:'',borderless:'',close:''},previewFileIcon:'',browseIcon:'',removeIcon:'',cancelIcon:'',pauseIcon:'',uploadIcon:'',msgValidationErrorIcon:' '}}(window.jQuery); diff --git a/resources/static/thumbnail.png b/resources/static/thumbnail.png new file mode 100644 index 0000000..91575aa Binary files /dev/null and b/resources/static/thumbnail.png differ diff --git a/resources/static/trash-icon.png b/resources/static/trash-icon.png new file mode 100644 index 0000000..abd7eb5 Binary files /dev/null and b/resources/static/trash-icon.png differ diff --git a/resources/static/upload-icon.png b/resources/static/upload-icon.png new file mode 100644 index 0000000..881940e Binary files /dev/null and b/resources/static/upload-icon.png differ diff --git a/server.js b/server.js new file mode 100644 index 0000000..6d81300 --- /dev/null +++ b/server.js @@ -0,0 +1,71 @@ +var express = require('express'); +var app = express(); +var router = express.Router(); +var upload = require('./app/config/multer.config.js'); + +const {Docker} = require('node-docker-api'); + +const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + +global.__basedir = __dirname; + +app.use(express.static('resources')); + +var http = require('http').createServer(app); +var io = require('socket.io')(http); + +var docker_build = (socket, sessionID) => { + console.log('[server][start]', sessionID); + socket.emit('start') + + docker.container.create({ + Image: 'python:3.8', + name: sessionID, + Cmd: [ '/bin/bash', '-c', 'ls /output' ], + Volumes: { + "/output": `./uploads / {sessionID}` + }, + }) + .then(container => container.logs({ + follow: true, + stdout: true, + stderr: true + })) + .then(stream => { + stream.on('data', info => console.log(info)) + stream.on('error', err => console.log(err)) + }) + .catch(error => console.log(error)); +}; + +var clients = 0; + +io.on('connection', (socket) => { + const sessionID = socket.id; + console.log('[client][connection]', sessionID); + clients = clients + 1; + io.sockets.emit('users_count', clients); + + socket.on('disconnect', () => { + clients = clients - 1; + console.log('[client][disconnect]', sessionID) + io.sockets.emit('users_count', clients); + }); + + socket.on('build', () => { + console.log('[client][build]', sessionID); + docker_build(socket, sessionID); + }); + +}); + +require('./app/routers/file.router.js')(app, router, upload); + +// Create a Server +var server = http.listen(8081, () => { + + var host = server.address().address + var port = server.address().port + + console.log("App listening at http://%s:%s", host, port); +}) diff --git a/views/404.html b/views/404.html new file mode 100644 index 0000000..8498913 --- /dev/null +++ b/views/404.html @@ -0,0 +1,18 @@ + + + + 404 Error! + + + + + + + + +
+

404 Error!

+

PAGE NOT FOUND

+
+ + \ No newline at end of file diff --git a/views/index.html b/views/index.html new file mode 100644 index 0000000..ea5d2d1 --- /dev/null +++ b/views/index.html @@ -0,0 +1,348 @@ + + + + COM2014 TSP Workshop + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
Traveling Salesman Problem
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
Leaderboard
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#ulysses16.tspatt48.tspst70.tspa280.tsppcb442.tspdsj1000.tsp
1InfInfInfInfInfInf
2InfInfInfInfInfInf
3InfInfInfInfInfInf
4InfInfInfInfInfInf
5InfInfInfInfInfInf
6InfInfInfInfInfInf
7InfInfInfInfInfInf
8InfInfInfInfInfInf
9InfInfInfInfInfInf
10InfInfInfInfInfInf
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+

Your Submission

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProblemYour SolutionBest Solution
ulysses16.tspInfInf + +
att48.tspInfInf + +
st70.tspInfInf + +
a280.tspInfInf + +
pcb442.tspInfInf + +
dsj1000.tspInfInf + +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + +
+
+
+
+
+
+
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file