Add/Update/Remove
This commit is contained in:
360
app/app.js
Normal file
360
app/app.js
Normal file
@@ -0,0 +1,360 @@
|
||||
port = 8000;
|
||||
|
||||
var log = require('loglevel');
|
||||
|
||||
log.setLevel(log.levels.INFO);
|
||||
|
||||
// Require the libraries:
|
||||
var SocketIOFileUpload = require('socketio-file-upload'),
|
||||
socketio = require('socket.io'),
|
||||
express = require('express');
|
||||
|
||||
const app = express()
|
||||
.use(SocketIOFileUpload.router)
|
||||
.use(express.static(__dirname + "/public"));
|
||||
const http = require('http');
|
||||
const server = http.createServer(app);
|
||||
const { Server } = require("socket.io");
|
||||
const { SqliteHandler } = require('./handlers/sqliteHandler');
|
||||
const { UploadObject } = require('./handlers/uploadHandler');
|
||||
|
||||
// Prepare directories
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
tmp = __dirname + "/tmp/";
|
||||
uploadsdir = __dirname + "/uploads/";
|
||||
|
||||
if (!fs.existsSync(tmp)){
|
||||
fs.mkdirSync(tmp);
|
||||
} else {
|
||||
for (const file of fs.readdirSync(tmp)) {
|
||||
fs.unlinkSync(path.join(tmp, file));
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(uploadsdir)){
|
||||
fs.mkdirSync(uploadsdir);
|
||||
}
|
||||
|
||||
const io = new Server(server);
|
||||
|
||||
|
||||
sqliteobj = new SqliteHandler("database.sqlite");
|
||||
db = sqliteobj.getDatabaseObj();
|
||||
|
||||
// Make your Express server:
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(__dirname + "/forms/select.html");
|
||||
})
|
||||
|
||||
app.get('/api', (req, res) => {
|
||||
res.status(200).json({status:"ok", code:200})
|
||||
})
|
||||
|
||||
app.get('/api/download/:hash', (req, res) => {
|
||||
if(handler == undefined) {
|
||||
var handler = new UploadObject(db);
|
||||
}
|
||||
if(handler.load(req.params.hash)){
|
||||
res.status(200).json({result: {
|
||||
files: handler.getFiles()
|
||||
}, code:200})
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/download/:hash/index/:index', (req, res) => {
|
||||
if(handler == undefined) {
|
||||
var handler = new UploadObject(db);
|
||||
}
|
||||
if(handler.load(req.params.hash) && typeof parseInt(req.params.index) == 'number') {
|
||||
res.set("Content-Disposition", "attachment; filename=\""+handler.getFiles()[req.params.index].filename+"\"")
|
||||
res.sendFile(__dirname + "/uploads/" + handler.getHash()+"/"+handler.getFiles()[req.params.index].filename);
|
||||
} else {
|
||||
notFound(req, res);
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/upload', (req, res) => {
|
||||
res.sendFile(__dirname + "/forms/upload.html");
|
||||
})
|
||||
|
||||
app.get('/download', (req, res) => {
|
||||
res.sendFile(__dirname + "/forms/download.html");
|
||||
})
|
||||
|
||||
app.get('/download/:hash', (req, res) => {
|
||||
res.sendFile(__dirname + "/forms/download.html");
|
||||
})
|
||||
|
||||
app.get('/download/:hash/:adminhash', (req, res) => {
|
||||
res.sendFile(__dirname + "/forms/download.html");
|
||||
})
|
||||
|
||||
// app.get('/download/:hash', (req, res) => {
|
||||
// if(handler == undefined) {
|
||||
// var handler = new UploadObject(db);
|
||||
// }
|
||||
// console.log(handler)
|
||||
// handler.load(req.params.hash)
|
||||
// if(handler.isLoaded()) {
|
||||
// res.send("Found");
|
||||
// } else {
|
||||
// notFound();
|
||||
// }
|
||||
|
||||
|
||||
// })
|
||||
// app.get('/download/:hash/:adminhash', (req, res) => {
|
||||
// handler.load(req.params.hash, req.params.adminhash)
|
||||
// res.send("sdadsd");
|
||||
// })
|
||||
|
||||
|
||||
// Start up Socket.IO:
|
||||
// var io = new socketio.Server(app.listen(port));
|
||||
io.sockets.on("connection", function(socket){
|
||||
var handler = new UploadObject(db);
|
||||
if(socket.handshake.url.startsWith("/socket.io")) {
|
||||
|
||||
var uploader = new SocketIOFileUpload();
|
||||
uploader.dir = "tmp";
|
||||
|
||||
|
||||
uploader.listen(socket);
|
||||
var processed = 0;
|
||||
socket.on("prepareUpload", function(num_files){
|
||||
console.log("Got \"prepareUpload\" event");
|
||||
console.log(num_files," Files")
|
||||
if(handler.new(num_files)){
|
||||
console.log("Emitting \"serverReady\" event");
|
||||
socket.emit("serverReady");
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
uploader.uploadValidator = function(event, callback){
|
||||
// asynchronous operations allowed here; when done,
|
||||
if (handler.isLoaded()) {
|
||||
callback(true);
|
||||
} else {
|
||||
callback(false);
|
||||
}
|
||||
};
|
||||
|
||||
uploader.on("start", function(event){
|
||||
// console.log(handler);
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Do something when a file is saved:
|
||||
uploader.on("saved", function(event){
|
||||
if (handler.isLoaded()) {
|
||||
processed++;
|
||||
if (event.file.success) {
|
||||
fs.renameSync(event.file.pathName, "uploads/"+handler.getDir()+"/"+event.file.name)
|
||||
handler.registerFile(event.file.name);
|
||||
} else {
|
||||
fs.unlinkSync(event.file.pathName);
|
||||
}
|
||||
console.log("Process Counter Updated", processed);
|
||||
console.log(handler.getNumFiles());
|
||||
if (handler.getNumFiles() == processed && handler.getNumFiles() > 0) {
|
||||
socket.emit("linkCreated", handler.getHash(), handler.getAdminHash());
|
||||
}
|
||||
}
|
||||
});
|
||||
uploader.on("complete", function(event){
|
||||
|
||||
});
|
||||
// Error handler:
|
||||
uploader.on("error", function(event){
|
||||
console.log("Error from uploader", event);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function notFound(req, res) {
|
||||
res.status(404);
|
||||
ans = { error: 'Not found', code: 404 }
|
||||
if (req.query.format == "xml"){
|
||||
res.set('Content-Type', 'text/xml');
|
||||
res.send(xml({xml: ans}));
|
||||
} else if (req.query.format == "csv") {
|
||||
res.send(ans.error);
|
||||
} else if (req.query.format == "json") {
|
||||
res.json(ans);
|
||||
}
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html') && !req.url.startsWith("/api")) {
|
||||
res.send('<h1>404 Not found</h1>');
|
||||
return;
|
||||
}
|
||||
|
||||
// respond with json
|
||||
if (req.accepts('json')) {
|
||||
res.json({ error: 'Not found', code: 404 });
|
||||
return;
|
||||
}
|
||||
|
||||
// default to plain-text. send()
|
||||
res.send('404');
|
||||
}
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
notFound(req,res)
|
||||
});
|
||||
|
||||
|
||||
|
||||
server.listen(port, () => {
|
||||
log.info("Server listening on *:"+port);
|
||||
});
|
||||
|
||||
|
||||
// https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
|
||||
function makeid(length) {
|
||||
let result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < length) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// const express = require('express');
|
||||
// const app = express()
|
||||
// const server = require('http').createServer(app);
|
||||
// var siofu = require("socketio-file-upload");
|
||||
// const port = process.env.PORT || 8080;
|
||||
// const io = require('socket.io')(server);
|
||||
// const fs = require("fs")
|
||||
|
||||
// app.use(express.static("public"));
|
||||
|
||||
|
||||
// io.on("connection", (socket) => {
|
||||
// console.log("req")
|
||||
// var uploader = new siofu();
|
||||
// uploader.dir = "./uploads";
|
||||
// uploader.listen(socket);
|
||||
// });
|
||||
|
||||
// app.listen(8000, () => {
|
||||
// console.log(`Server started...`);
|
||||
// });
|
||||
|
||||
|
||||
// server.listen(port, function() {
|
||||
// console.log(`Listening on port ${port}`);
|
||||
// });
|
||||
|
||||
|
||||
// const express = require('express');
|
||||
// const app = express();
|
||||
// const fs = require("fs")
|
||||
// const s = require("socket.io");
|
||||
|
||||
// const io = new s.Server();
|
||||
|
||||
// io.on("connection", (socket) => {
|
||||
// socket.on("upload", (file, callback) => {
|
||||
// console.log(file); // <Buffer 25 50 44 ...>
|
||||
|
||||
// // save the content to the disk, for example
|
||||
// fs.writeFile("./tmp/upload", file, (err) => {
|
||||
// callback({ message: err ? "failure" : "success" });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// // const multer = require("multer");
|
||||
|
||||
// // const date = Date.now();
|
||||
|
||||
// // const storage = multer.diskStorage({
|
||||
// // destination: function (req, file, cb) {
|
||||
// // if(req.body.name == "test"){
|
||||
// // storage.error("Already Exists");
|
||||
// // }
|
||||
// // const nameFolder = date + "-" +req.body.name;
|
||||
// // const dirRep = nameFolder.replace(/\s/g, '');
|
||||
// // const folderPath = "lessons/"+dirRep+"/";
|
||||
// // fs.mkdirSync(folderPath, {recursive: true})
|
||||
// // cb(null, folderPath)
|
||||
// // },
|
||||
// // filename: function (req, file, cb) {
|
||||
// // const filename = Date.now() + "-" + file.originalname;
|
||||
// // const rep = filename.replace(/\s/g, '')
|
||||
// // cb(null, rep)
|
||||
// // },
|
||||
// // });
|
||||
|
||||
// // const upload = multer({
|
||||
// // storage: storage,
|
||||
// // limits: {
|
||||
// // fileSize: 50000000000
|
||||
// // }
|
||||
// // });
|
||||
// app.use(express.urlencoded({ extended: true }));
|
||||
// app.use(express.static('public'));
|
||||
|
||||
// app.get('/download/:hash', (req, res) => {
|
||||
|
||||
// })
|
||||
|
||||
// // app.post("/upload", upload.array("files"), uploadFiles);
|
||||
|
||||
// // function uploadFiles(req, res) {
|
||||
// // console.log(req.body);
|
||||
// // console.log(req.files);
|
||||
// // res.json({ message: "Successfully uploaded files" });
|
||||
// // }
|
||||
|
||||
// function checkPerm(req, res) {
|
||||
// if(req.body.name == "kuba"){
|
||||
// throw("Invalid name")
|
||||
// }else {
|
||||
// return req, res
|
||||
// }
|
||||
// }
|
||||
|
||||
// app.use(function (req, res, next) {
|
||||
// res.status(404);
|
||||
// console.log(req.query);
|
||||
// ans = { error: 'Not found', code: 404 }
|
||||
// if (req.query.format == "xml"){
|
||||
// res.set('Content-Type', 'text/xml');
|
||||
// res.send(xml({xml: ans}));
|
||||
// } else if (req.query.format == "csv") {
|
||||
// res.send(ans.error);
|
||||
// } else if (req.query.format == "json") {
|
||||
// res.json(ans);
|
||||
// }
|
||||
|
||||
// // respond with html page
|
||||
// if (req.accepts('html') && !req.url.startsWith("/api")) {
|
||||
// res.send('<h1>404 Not found</h1>');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // respond with json
|
||||
// if (req.accepts('json')) {
|
||||
// res.json({ error: 'Not found', code: 404 });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // default to plain-text. send()
|
||||
// res.send('404');
|
||||
// });
|
||||
|
||||
// app.listen(5000, () => {
|
||||
// console.log(`Server started...`);
|
||||
// });
|
||||
82
app/forms/select.html
Normal file
82
app/forms/select.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kakubovna: Select</title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geologica:wght@800&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="flex-container">
|
||||
<div id="select" class="container vertical">
|
||||
<div class="card-button-box vertical">
|
||||
<div class="card-button" onclick="window.location.href = document.URL+'upload'"><img src="/img/add-sharp-white.svg" alt="Upload"><p id="button-selection-newupload">New Upload</div>
|
||||
<div class="card-button" onclick="window.location.href = document.URL+'download'"><img src="/img/arrow-down-white.svg" alt="Download"><p id="button-selection-download">Download</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <canvas id="c" style="width: 100%; height: 100%; position: absolute;"></canvas> -->
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// geting canvas by Boujjou Achraf
|
||||
var c = document.getElementById("c");
|
||||
var ctx = c.getContext("2d");
|
||||
|
||||
//making the canvas full screen
|
||||
c.height = window.innerHeight;
|
||||
c.width = window.innerWidth;
|
||||
|
||||
//chinese characters - taken from the unicode charset
|
||||
var matrix = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789@#$%^&*()*&^%+-/~{[|`]}";
|
||||
//converting the string into an array of single characters
|
||||
matrix = matrix.split("");
|
||||
|
||||
var font_size = 10;
|
||||
var columns = c.width / font_size; //number of columns for the rain
|
||||
//an array of drops - one per column
|
||||
var drops = [];
|
||||
//x below is the x coordinate
|
||||
//1 = y co-ordinate of the drop(same for every drop initially)
|
||||
for (var x = 0; x < columns; x++)
|
||||
drops[x] = 1;
|
||||
|
||||
//drawing the characters
|
||||
function draw() {
|
||||
rainbow = ['#ff0000', '#ffa500', '#ffff00', '#008000', '#00FFFF', '#4b0082', '#ee82ee'];
|
||||
//Black BG for the canvas
|
||||
//translucent BG to show trail
|
||||
ctx.fillStyle = "rgba(0, 0, 0, 0.04)";
|
||||
ctx.fillRect(0, 0, c.width, c.height);
|
||||
|
||||
ctx.fillStyle = rainbow[Math.floor(Math.random() * 5)];//green text
|
||||
ctx.font = font_size + "px arial";
|
||||
//looping over drops
|
||||
for (var i = 0; i < drops.length; i++) {
|
||||
//a random chinese character to print
|
||||
var text = matrix[Math.floor(Math.random() * matrix.length)];
|
||||
//x = i*font_size, y = value of drops[i]*font_size
|
||||
ctx.fillText(text, i * font_size, drops[i] * font_size);
|
||||
|
||||
//sending the drop back to the top randomly after it has crossed the screen
|
||||
//adding a randomness to the reset to make the drops scattered on the Y axis
|
||||
if (drops[i] * font_size > c.height && Math.random() > 0.975)
|
||||
drops[i] = 0;
|
||||
|
||||
//incrementing Y coordinate
|
||||
drops[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(draw, 35);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
98
app/forms/upload.html
Normal file
98
app/forms/upload.html
Normal file
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kakubovna: Upload</title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geologica:wght@800&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="flex-container">
|
||||
<div id="upload" class="container vertical">
|
||||
<div class="dropZoneContainer">
|
||||
<input type="file" title="" id="drop_zone" class="FileUpload" onchange="addFiles(this.files)" multiple />
|
||||
<div class="dropZoneOverlay">
|
||||
<p>Drag and drop your files <br />or<br />Click to add</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<table class="filelist"></table>
|
||||
</div>
|
||||
<button style="vertical-align:middle" class="button" onclick="startUpload()"><span>Upload </span></button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/socket.io.js"></script>
|
||||
<script src="js/siofu-client.js"></script>
|
||||
<script>
|
||||
var selectedFiles = [];
|
||||
|
||||
function addFiles(files) {
|
||||
Array.from(files).forEach(file => {
|
||||
addFile(file);
|
||||
});
|
||||
}
|
||||
|
||||
function addFile(file) {
|
||||
console.log(selectedFiles.find((element) => element.name == file.name))
|
||||
if (selectedFiles.find((element) => element.name == file.name) == undefined) {
|
||||
selectedFiles.push(file);
|
||||
}
|
||||
updateFileList();
|
||||
}
|
||||
|
||||
function shortString(str, maxlen = 13) {
|
||||
if (str.length > maxlen) {
|
||||
shortstr = str.slice(0, (maxlen - 3) / 2) + "..." + str.slice(-(maxlen - 3) / 2);
|
||||
} else {
|
||||
shortstr = str;
|
||||
}
|
||||
return shortstr;
|
||||
}
|
||||
|
||||
function updateFileList(maxlen = 30) {
|
||||
filelist = document.getElementsByClassName("filelist")[0];
|
||||
filelist.innerHTML = "";
|
||||
i = 0
|
||||
selectedFiles.forEach(file => {
|
||||
filelist.innerHTML += "<tr title=\"" + file.name + "\"><td>" + shortString(file.name, maxlen) + "</td></tr>";
|
||||
i++;
|
||||
});
|
||||
}
|
||||
|
||||
var socket = io.connect(
|
||||
{
|
||||
forceNew: true,
|
||||
extraHeaders: {
|
||||
Authorization: "bsadsds"
|
||||
}
|
||||
}
|
||||
);
|
||||
var uploader = new SocketIOFileUpload(socket);
|
||||
uploader.addEventListener("choose", function (event) {
|
||||
console.log("Files Chosen: " + event.files);
|
||||
});
|
||||
uploader.addEventListener("progress", function (event) {
|
||||
console.log("Files Chosen: " + event.bytesLoaded / event.file.size);
|
||||
});
|
||||
|
||||
function startUpload() {
|
||||
socket.once("serverReady", function (event) {
|
||||
console.log("Got \"serverReady\" event");
|
||||
console.log("Uploading..");
|
||||
uploader.submitFiles(selectedFiles);
|
||||
})
|
||||
console.log("Emitting \"prepareUpload\" event");
|
||||
socket.emit("prepareUpload", selectedFiles.length);
|
||||
}
|
||||
socket.once("linkCreated", (hash, adminhash) => {
|
||||
console.log("Link Created");
|
||||
window.location.href = document.location.protocol + "//" + document.location.host + "/api/download/" + hash + '/'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
44
app/handlers/sqliteHandler.js
Normal file
44
app/handlers/sqliteHandler.js
Normal file
@@ -0,0 +1,44 @@
|
||||
module.exports = {
|
||||
|
||||
SqliteHandler: class SqliteHandler {
|
||||
constructor(path) {
|
||||
this.loaded = false;
|
||||
this.path = path;
|
||||
this.db = require('better-sqlite3')(this.path);
|
||||
this.db.prepare(`CREATE TABLE IF NOT EXISTS "user" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"name" TEXT NOT NULL UNIQUE,
|
||||
"password" TEXT NOT NULL,
|
||||
"email" INTEGER,
|
||||
"admin" INTEGER,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
)`).run();
|
||||
|
||||
this.db.prepare(`CREATE TABLE IF NOT EXISTS "upload" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"hashsalt" TEXT NOT NULL UNIQUE,
|
||||
"adminhash" TEXT NOT NULL UNIQUE,
|
||||
"desc" TEXT,
|
||||
"password" TEXT,
|
||||
"owner_id" INTEGER,
|
||||
"upload_unix" INTEGER,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("owner_id") REFERENCES "user"("id")
|
||||
)`).run();
|
||||
|
||||
this.db.prepare(`CREATE TABLE IF NOT EXISTS "file" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"filename" TEXT NOT NULL,
|
||||
"upload_id" INTEGER NOT NULL,
|
||||
"state" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("upload_id") REFERENCES "upload"("id")
|
||||
)`).run();
|
||||
}
|
||||
|
||||
getDatabaseObj() {
|
||||
return this.db;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
158
app/handlers/uploadHandler.js
Normal file
158
app/handlers/uploadHandler.js
Normal file
@@ -0,0 +1,158 @@
|
||||
module.exports = {
|
||||
|
||||
UploadObject: class UploadObject {
|
||||
constructor(db) {
|
||||
this.db = db;
|
||||
this.absrc = ['E', 'T', 'X', 'W', 'U', 'R', 'J', 'I', 'N', 'C'];
|
||||
this.loaded = false;
|
||||
this.num_files = 0;
|
||||
this.fileCache = {};
|
||||
}
|
||||
|
||||
new(num_files) {
|
||||
if (!this.loaded) {
|
||||
if (typeof num_files == "number" && num_files > 0) {
|
||||
this.num_files = num_files;
|
||||
} else {
|
||||
console.error("Invalid number of files");
|
||||
return false;
|
||||
}
|
||||
this.hashsalt = this.makeHash(10);
|
||||
this.adminhash = this.makeHash(10);
|
||||
var sql = this.db.prepare(`INSERT INTO "upload" (hashsalt, adminhash, upload_unix) VALUES (?, ?, ?)`);
|
||||
var result = sql.run(this.hashsalt, this.adminhash, Math.floor(Date.now() / 1000));
|
||||
this.id = result.lastInsertRowid;
|
||||
this.hashid = this.id2hashid(result.lastInsertRowid);
|
||||
this.dir = this.hashid + this.hashsalt;
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync("uploads/" + this.dir)) {
|
||||
fs.mkdirSync("uploads/" + this.dir);
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
load(hash) {
|
||||
if (!this.loaded) {
|
||||
var shash = this.splitHash(hash);
|
||||
this.hashid = shash[0];
|
||||
this.id = this.hashid2id(this.hashid)
|
||||
this.hashsalt = shash[1];
|
||||
this.dir = this.hashid + this.hashsalt;
|
||||
|
||||
var sql = this.db.prepare(`SELECT count(id) AS "count" FROM "file" WHERE upload_id=?`);
|
||||
var num_files = sql.get(this.id)["count"];
|
||||
if (typeof num_files == "number" && num_files > 0) {
|
||||
this.num_files = num_files;
|
||||
} else {
|
||||
console.error("Invalid number of files");
|
||||
return false;
|
||||
}
|
||||
|
||||
var sql = this.db.prepare(`SELECT EXISTS(SELECT 1 FROM "upload" WHERE id=? AND hashsalt=? LIMIT 1) AS "exists"`);
|
||||
var result = sql.get(this.id, this.hashsalt);
|
||||
|
||||
|
||||
|
||||
if (result["exists"]) {
|
||||
console.log("LOADED: ", result);
|
||||
this.loaded = true;
|
||||
return true;
|
||||
} else {
|
||||
console.log("Not Found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getNumFiles() {
|
||||
return this.num_files;
|
||||
}
|
||||
|
||||
isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
getDir() {
|
||||
return this.dir;
|
||||
}
|
||||
|
||||
loadFiles() {
|
||||
this.files = this.db.prepare(`SELECT filename FROM "file" WHERE upload_id=?`).all(this.id);
|
||||
console.log(this.files);
|
||||
}
|
||||
|
||||
getFiles() {
|
||||
this.loadFiles();
|
||||
return this.files;
|
||||
}
|
||||
|
||||
getFileByIndex(index) {
|
||||
return this.getFiles()[index];
|
||||
}
|
||||
|
||||
registerFile(filename) {
|
||||
var sql = this.db.prepare(`INSERT INTO "file" (filename, upload_id) VALUES (?, ?)`);
|
||||
var result = sql.run(filename, this.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
delete() {
|
||||
|
||||
}
|
||||
|
||||
getHash() {
|
||||
return this.hashid + this.hashsalt;
|
||||
}
|
||||
|
||||
getHashId() {
|
||||
return this.hashid;
|
||||
}
|
||||
|
||||
getAdminHash() {
|
||||
return this.adminhash;
|
||||
}
|
||||
|
||||
|
||||
id2hashid(id) {
|
||||
var hashid = "";
|
||||
id = id.toString();
|
||||
for (var i = 0; i < id.length; i++) {
|
||||
hashid += this.absrc[id.charAt(i)];
|
||||
}
|
||||
return hashid;
|
||||
}
|
||||
|
||||
hashid2id(hashid) {
|
||||
var id = "";
|
||||
for (var i = 0; i < hashid.length; i++) {
|
||||
var index = this.absrc.findIndex((letter) => letter == hashid.charAt(i));
|
||||
if (!(index < 0)) {
|
||||
id = id + index.toString();
|
||||
};
|
||||
}
|
||||
return parseInt(id);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
|
||||
makeHash(len = 10) {
|
||||
let result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < len) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
splitHash(hash) {
|
||||
return [hash.slice(0, hash.length - 10), hash.slice(hash.length - 10, hash.length)]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
18
app/package.json
Normal file
18
app/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "kakubovna",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Dzejkobik",
|
||||
"license": "",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^8.4.0",
|
||||
"express": "^4.18.2",
|
||||
"loglevel": "^1.8.1",
|
||||
"socket.io": "^4.6.2",
|
||||
"socketio-file-upload": "^0.7.3"
|
||||
}
|
||||
}
|
||||
174
app/public/css/style.css
Normal file
174
app/public/css/style.css
Normal file
@@ -0,0 +1,174 @@
|
||||
@charset "UTF-8";
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #1b1b1b;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.box__dragndrop,
|
||||
.box__uploading,
|
||||
.box__success,
|
||||
.box__error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
font-family: "Geologica", sans-serif;
|
||||
border-radius: 15px;
|
||||
border-color: black;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
background-color: #f16d01;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.filelist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.filelist p {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.list p {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
#select {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
background-color: #e42200;
|
||||
border: none;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
padding: 20px;
|
||||
width: 200px;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.button span {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.button span:after {
|
||||
content: "»";
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
right: -20px;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.button:hover span {
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.button:hover span:after {
|
||||
opacity: 1;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.card-button-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 50px;
|
||||
}
|
||||
.card-button-box .card-button {
|
||||
display: flex;
|
||||
background-color: rgba(88, 88, 88, 0.2);
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
height: 100px;
|
||||
width: 400px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
transition: 0.3s;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card-button-box .card-button img {
|
||||
height: 100%;
|
||||
flex-basis: 40%;
|
||||
}
|
||||
.card-button-box .card-button p {
|
||||
flex-basis: 60%;
|
||||
color: #FFFFFF;
|
||||
text-align: left;
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
.card-button:hover {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.dropZoneContainer {
|
||||
display: grid;
|
||||
margin-left: 100px;
|
||||
margin-right: 100px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.dropZoneOverlay, .FileUpload {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.dropZoneOverlay {
|
||||
margin-top: -3px;
|
||||
margin-left: -3px;
|
||||
border: dotted 3px;
|
||||
font-size: 110%;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.FileUpload {
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}/*# sourceMappingURL=style.css.map */
|
||||
1
app/public/css/style.css.map
Normal file
1
app/public/css/style.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["style.css","style.scss"],"names":[],"mappings":"AAAA,gBAAgB;ACAhB;;EAEE,UAAA;EACA,SAAA;EACA,YAAA;ADEF;;ACEA;EACE,yBAAA;ADCF;;ACGA;EACE,cAAA;ADAF;;ACGA;;;;EAIE,aAAA;ADAF;;ACGA;EACE,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ADAF;;ACGA;EACE,oCAAA;EACA,mBAAA;EACA,mBAAA;EACA,iBAAA;EACA,mBAAA;EACA,yBAAA;EACA,aAAA;EACA,aAAA;EACA,kBAAA;ADAF;;ACGA;EACI,sBAAA;EACA,uBAAA;EACA,mBAAA;ADAJ;;ACGA;EACI,gBAAA;ADAJ;;ACGA;EACI,aAAA;EACA,sBAAA;EACA,UAAA;ADAJ;;ACEA;EACI,cAAA;ADCJ;;ACEA;EACI,cAAA;ADCJ;;ACEA;EACI,sBAAA;ADCJ;;ACEA;EACI,qBAAA;EACA,kBAAA;EACA,yBAAA;EACA,YAAA;EACA,cAAA;EACA,kBAAA;EACA,eAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,eAAA;EACA,WAAA;ADCJ;;ACEA;EACI,eAAA;EACA,qBAAA;EACA,kBAAA;EACA,gBAAA;ADCJ;;ACEA;EACI,YAAA;EACA,kBAAA;EACA,UAAA;EACA,MAAA;EACA,YAAA;EACA,gBAAA;ADCJ;;ACEA;EACI,mBAAA;ADCJ;;ACEA;EACI,UAAA;EACA,QAAA;ADCJ;;ACEA;EACI,aAAA;EACA,uBAAA;EACA,SAAA;ADCJ;ACAI;EACI,aAAA;EACA,uCAAA;EACA,kBAAA;EACA,aAAA;EACA,aAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,gBAAA;EACA,mBAAA;EACA,uBAAA;EACA,eAAA;ADER;ACDQ;EACI,YAAA;EACA,eAAA;ADGZ;ACAQ;EACI,eAAA;EACA,cAAA;EACA,gBAAA;EACA,mBAAA;ADEZ;;ACOA;EACI,sBAAA;EACA,2CAAA;ADJJ;;ACOA;EACI,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,WAAA;EACA,aAAA;ADJJ;;ACOA;EACI,YAAA;EACA,WAAA;EACA,cAAA;EACA,WAAA;ADJJ;;ACOA;EACI,gBAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,sBAAA;ADJJ;;ACMA;EACI,UAAA;EACA,eAAA;ADHJ","file":"style.css"}
|
||||
178
app/public/css/style.scss
Normal file
178
app/public/css/style.scss
Normal file
@@ -0,0 +1,178 @@
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
background-color: #1b1b1b;
|
||||
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.box__dragndrop,
|
||||
.box__uploading,
|
||||
.box__success,
|
||||
.box__error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
font-family: 'Geologica', sans-serif;
|
||||
border-radius: 15px;
|
||||
border-color: black;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
background-color: #f16d01;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.filelist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 0;
|
||||
}
|
||||
.filelist p {
|
||||
color:#FFFFFF;
|
||||
}
|
||||
|
||||
.list p {
|
||||
color:#FFFFFF;
|
||||
}
|
||||
|
||||
#select {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
background-color: #e42200;
|
||||
border: none;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
padding: 20px;
|
||||
width: 200px;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.button span {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.button span:after {
|
||||
content: '\00bb';
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
right: -20px;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.button:hover span {
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.button:hover span:after {
|
||||
opacity: 1;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.card-button-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 50px;
|
||||
.card-button {
|
||||
display: flex;
|
||||
background-color: rgba(88, 88, 88, 0.2);
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
height: 100px;
|
||||
width: 400px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.3s;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
img {
|
||||
height: 100%;
|
||||
flex-basis: 40%;
|
||||
}
|
||||
|
||||
p {
|
||||
flex-basis: 60%;
|
||||
color:#FFFFFF;
|
||||
text-align: left;
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.card-button:hover {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.dropZoneContainer {
|
||||
display: grid;
|
||||
margin-left: 100px;
|
||||
margin-right: 100px;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.dropZoneOverlay, .FileUpload {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.dropZoneOverlay {
|
||||
margin-top: -3px;
|
||||
margin-left: -3px;
|
||||
border: dotted 3px;
|
||||
font-size: 110%;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.FileUpload {
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
1
app/public/img/add-sharp-white.svg
Normal file
1
app/public/img/add-sharp-white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" color="#FFFFFF" class="ionicon" viewBox="0 0 512 512"><path color="#FFFFFF" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round" stroke-width="32" d="M256 112v288M400 256H112"/></svg>
|
||||
|
After Width: | Height: | Size: 252 B |
1
app/public/img/arrow-down-white.svg
Normal file
1
app/public/img/arrow-down-white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" color="#FFFFFF" class="ionicon" viewBox="0 0 512 512"><path color="#FFFFFF" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M112 268l144 144 144-144M256 392V100"/></svg>
|
||||
|
After Width: | Height: | Size: 264 B |
771
app/public/js/siofu-client.js
Normal file
771
app/public/js/siofu-client.js
Normal file
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Shane Carr and others
|
||||
* X11 License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the names of the authors or copyright
|
||||
* holders shall not be used in advertising or otherwise to promote the sale,
|
||||
* use or other dealings in this Software without prior written authorization
|
||||
* from the authors or copyright holders.
|
||||
*/
|
||||
|
||||
// Do not check function indentation because this is intentionally ignored in order to preserve history in git.
|
||||
/* eslint-disable indent */
|
||||
|
||||
/*
|
||||
* A client-side JavaScript object to handle file uploads to a Node.JS server
|
||||
* via Socket.IO.
|
||||
* @implements EventTarget
|
||||
* @param {SocketIO} socket The current Socket.IO connection.
|
||||
*/
|
||||
(function (scope, name, factory) {
|
||||
/* eslint-disable no-undef */
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define([], factory);
|
||||
}
|
||||
else if (typeof module === "object" && module.exports) {
|
||||
module.exports = factory();
|
||||
}
|
||||
else {
|
||||
scope[name] = factory();
|
||||
}
|
||||
/* eslint-enable no-undef */
|
||||
}(this, "SocketIOFileUpload", function () {
|
||||
return function (socket, options) {
|
||||
"use strict";
|
||||
|
||||
var self = this; // avoids context issues
|
||||
|
||||
// Check for compatibility
|
||||
if (!window.File || !window.FileReader) {
|
||||
throw new Error("Socket.IO File Upload: Browser Not Supported");
|
||||
}
|
||||
|
||||
if ( !window.siofu_global ) {
|
||||
window.siofu_global = {
|
||||
instances: 0,
|
||||
downloads: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Private and Public Variables
|
||||
var callbacks = {},
|
||||
uploadedFiles = {},
|
||||
chunkCallbacks = {},
|
||||
readyCallbacks = {},
|
||||
communicators = {};
|
||||
|
||||
var _getOption = function (key, defaultValue) {
|
||||
if(!options) {
|
||||
return defaultValue;
|
||||
}
|
||||
return options[key] || defaultValue;
|
||||
};
|
||||
|
||||
self.fileInputElementId = "siofu_input_"+window.siofu_global.instances++;
|
||||
self.resetFileInputs = true;
|
||||
self.useText = _getOption("useText", false);
|
||||
self.serializedOctets = _getOption("serializedOctets", false);
|
||||
self.useBuffer = _getOption("useBuffer", true);
|
||||
self.chunkSize = _getOption("chunkSize", 1024 * 100); // 100kb default chunk size
|
||||
self.topicName = _getOption("topicName", "siofu");
|
||||
|
||||
/**
|
||||
* WrapData allow you to wrap the Siofu messages into a predefined format.
|
||||
* You can then easily use Siofu packages even in strongly typed topic.
|
||||
* wrapData can be a boolean or an object. It is false by default.
|
||||
* If wrapData is true it will allow you to send all the messages to only one topic by wrapping the siofu actions and messages.
|
||||
*
|
||||
* ex:
|
||||
{
|
||||
action: 'complete',
|
||||
message: {
|
||||
id: id,
|
||||
success: success,
|
||||
detail: fileInfo.clientDetail
|
||||
}
|
||||
}
|
||||
*
|
||||
* If wrapData is an object constituted of two mandatory key and one optional:
|
||||
* wrapKey and unwrapKey (mandatory): Corresponding to the key used to wrap the siofu data and message
|
||||
* additionalData (optional): Corresponding to the data to send along with file data
|
||||
*
|
||||
* ex:
|
||||
* if wrapData = {
|
||||
wrapKey: {
|
||||
action: 'actionType',
|
||||
message: 'data'
|
||||
},
|
||||
unwrapKey: {
|
||||
action: 'actionType',
|
||||
message: 'message'
|
||||
},
|
||||
additionalData: {
|
||||
acknowledgement: true
|
||||
}
|
||||
}
|
||||
* When Siofu will send for example a complete message this will send:
|
||||
*
|
||||
{
|
||||
acknowledgement: true,
|
||||
actionType: 'complete',
|
||||
data: {
|
||||
id: id,
|
||||
success: success,
|
||||
detail: fileInfo.clientDetail
|
||||
}
|
||||
}
|
||||
* and it's waiting from client data formatted like this:
|
||||
*
|
||||
{
|
||||
actionType: '...',
|
||||
message: {...}
|
||||
}
|
||||
* /!\ If wrapData is wrong configured is interpreted as false /!\
|
||||
*/
|
||||
self.wrapData = _getOption("wrapData", false);
|
||||
|
||||
var _isWrapDataWellConfigured = function () {
|
||||
if (typeof self.wrapData === "boolean") {
|
||||
return true;
|
||||
}
|
||||
if (typeof self.wrapData !== "object" || Array.isArray(self.wrapData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!self.wrapData.wrapKey || typeof self.wrapData.wrapKey.action !== "string" || typeof self.wrapData.wrapKey.message !== "string" ||
|
||||
!self.wrapData.unwrapKey || typeof self.wrapData.unwrapKey.action !== "string" || typeof self.wrapData.unwrapKey.message !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allow user to access to some private function to customize message reception.
|
||||
* This is used if you specified wrapOptions on the client side and have to manually bind message to callback.
|
||||
*/
|
||||
self.exposePrivateFunction = _getOption("exposePrivateFunction", false);
|
||||
|
||||
var _getTopicName = function (topicExtension) {
|
||||
if (self.wrapData) {
|
||||
return self.topicName;
|
||||
}
|
||||
|
||||
return self.topicName + topicExtension;
|
||||
};
|
||||
|
||||
var _wrapData = function (data, action) {
|
||||
if(!_isWrapDataWellConfigured() || !self.wrapData) {
|
||||
return data;
|
||||
}
|
||||
var dataWrapped = {};
|
||||
if(self.wrapData.additionalData) {
|
||||
Object.assign(dataWrapped, self.wrapData.additionalData);
|
||||
}
|
||||
|
||||
var actionKey = self.wrapData.wrapKey && typeof self.wrapData.wrapKey.action === "string" ? self.wrapData.wrapKey.action : "action";
|
||||
var messageKey = self.wrapData.wrapKey && typeof self.wrapData.wrapKey.message === "string" ? self.wrapData.wrapKey.message : "message";
|
||||
|
||||
dataWrapped[actionKey] = action;
|
||||
dataWrapped[messageKey] = data;
|
||||
return dataWrapped;
|
||||
};
|
||||
|
||||
/**
|
||||
* Private method to dispatch a custom event on the instance.
|
||||
* @param {string} eventName Name for which listeners can listen.
|
||||
* @param {object} properties An object literal with additional properties
|
||||
* to be attached to the event object.
|
||||
* @return {boolean} false if any callback returned false; true otherwise
|
||||
*/
|
||||
var _dispatch = function (eventName, properties) {
|
||||
var evnt = document.createEvent("Event");
|
||||
evnt.initEvent(eventName, false, false);
|
||||
for (var prop in properties) {
|
||||
if (properties.hasOwnProperty(prop)) {
|
||||
evnt[prop] = properties[prop];
|
||||
}
|
||||
}
|
||||
return self.dispatchEvent(evnt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Private method to bind an event listener. Useful to ensure that all
|
||||
* events have been unbound. Inspired by Backbone.js.
|
||||
*/
|
||||
var _listenedReferences = [];
|
||||
var _listenTo = function (object, eventName, callback, bubble) {
|
||||
object.addEventListener(eventName, callback, bubble);
|
||||
_listenedReferences.push(arguments);
|
||||
};
|
||||
var _stopListeningTo = function (object, eventName, callback, bubble) {
|
||||
if (object.removeEventListener) {
|
||||
object.removeEventListener(eventName, callback, bubble);
|
||||
}
|
||||
};
|
||||
var _stopListening = function () {
|
||||
for (var i = _listenedReferences.length - 1; i >= 0; i--) {
|
||||
_stopListeningTo.apply(this, _listenedReferences[i]);
|
||||
}
|
||||
_listenedReferences = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Private closure for the _load function.
|
||||
* @param {File} file A W3C File object
|
||||
* @return {void}
|
||||
*/
|
||||
var _loadOne = function (file) {
|
||||
// First check for file size
|
||||
if (self.maxFileSize !== null && file.size > self.maxFileSize) {
|
||||
_dispatch("error", {
|
||||
file: file,
|
||||
message: "Attempt by client to upload file exceeding the maximum file size",
|
||||
code: 1
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch an event to listeners and stop now if they don't want
|
||||
// this file to be uploaded.
|
||||
var evntResult = _dispatch("start", {
|
||||
file: file
|
||||
});
|
||||
if (!evntResult) return;
|
||||
|
||||
// Scope variables
|
||||
var reader = new FileReader(),
|
||||
id = window.siofu_global.downloads++,
|
||||
uploadComplete = false,
|
||||
useText = self.useText,
|
||||
offset = 0,
|
||||
newName;
|
||||
if (reader._realReader) reader = reader._realReader; // Support Android Crosswalk
|
||||
uploadedFiles[id] = file;
|
||||
|
||||
// An object for the outside to use to communicate with us
|
||||
var communicator = { id: id };
|
||||
|
||||
// Calculate chunk size
|
||||
var chunkSize = self.chunkSize;
|
||||
if (chunkSize >= file.size || chunkSize <= 0) chunkSize = file.size;
|
||||
|
||||
// Private function to handle transmission of file data
|
||||
var transmitPart = function (start, end, content) {
|
||||
var isBase64 = false;
|
||||
if (!useText) {
|
||||
try {
|
||||
var uintArr = new Uint8Array(content);
|
||||
|
||||
// Support the transmission of serialized ArrayBuffers
|
||||
// for experimental purposes, but default to encoding the
|
||||
// transmission in Base 64.
|
||||
if (self.serializedOctets) {
|
||||
content = uintArr;
|
||||
}
|
||||
else if (self.useBuffer) {
|
||||
content = uintArr.buffer;
|
||||
}
|
||||
else {
|
||||
isBase64 = true;
|
||||
content = _uint8ArrayToBase64(uintArr);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
socket.emit(_getTopicName("_done"), _wrapData({
|
||||
id: id,
|
||||
interrupt: true
|
||||
}, "done"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO override the send data
|
||||
socket.emit(_getTopicName("_progress"), _wrapData({
|
||||
id: id,
|
||||
size: file.size,
|
||||
start: start,
|
||||
end: end,
|
||||
content: content,
|
||||
base64: isBase64
|
||||
}, "progress"));
|
||||
};
|
||||
|
||||
// Callback when tranmission is complete.
|
||||
var transmitDone = function () {
|
||||
socket.emit(_getTopicName("_done"), _wrapData({
|
||||
id: id
|
||||
}, "done"));
|
||||
};
|
||||
|
||||
// Load a "chunk" of the file from offset to offset+chunkSize.
|
||||
//
|
||||
// Note that FileReader has its own "progress" event. However,
|
||||
// it has not proven to be reliable enough for production. See
|
||||
// Stack Overflow question #16713386.
|
||||
//
|
||||
// To compensate, we will manually load the file in chunks of a
|
||||
// size specified by the user in the uploader.chunkSize property.
|
||||
var processChunk = function () {
|
||||
// Abort if we are told to do so.
|
||||
if (communicator.abort) return;
|
||||
|
||||
var chunk = file.slice(offset, Math.min(offset+chunkSize, file.size));
|
||||
if (useText) {
|
||||
reader.readAsText(chunk);
|
||||
}
|
||||
else {
|
||||
reader.readAsArrayBuffer(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
// Callback for when the reader has completed a load event.
|
||||
var loadCb = function (event) {
|
||||
// Abort if we are told to do so.
|
||||
if (communicator.abort) return;
|
||||
|
||||
// Transmit the newly loaded data to the server and emit a client event
|
||||
var bytesLoaded = Math.min(offset+chunkSize, file.size);
|
||||
transmitPart(offset, bytesLoaded, event.target.result);
|
||||
_dispatch("progress", {
|
||||
file: file,
|
||||
bytesLoaded: bytesLoaded,
|
||||
name: newName
|
||||
});
|
||||
|
||||
// Get ready to send the next chunk
|
||||
offset += chunkSize;
|
||||
if (offset >= file.size) {
|
||||
// All done!
|
||||
transmitDone();
|
||||
_dispatch("load", {
|
||||
file: file,
|
||||
reader: reader,
|
||||
name: newName
|
||||
});
|
||||
uploadComplete = true;
|
||||
}
|
||||
};
|
||||
_listenTo(reader, "load", loadCb);
|
||||
|
||||
// Listen for an "error" event. Stop the transmission if one is received.
|
||||
_listenTo(reader, "error", function () {
|
||||
socket.emit(_getTopicName("_done"), _wrapData({
|
||||
id: id,
|
||||
interrupt: true
|
||||
}, "done"));
|
||||
_stopListeningTo(reader, "load", loadCb);
|
||||
});
|
||||
|
||||
// Do the same for the "abort" event.
|
||||
_listenTo(reader, "abort", function () {
|
||||
socket.emit(_getTopicName("_done"), _wrapData({
|
||||
id: id,
|
||||
interrupt: true
|
||||
}, "done"));
|
||||
_stopListeningTo(reader, "load", loadCb);
|
||||
});
|
||||
|
||||
// Transmit the "start" message to the server.
|
||||
socket.emit(_getTopicName("_start"), _wrapData({
|
||||
name: file.name,
|
||||
mtime: file.lastModified,
|
||||
meta: file.meta,
|
||||
size: file.size,
|
||||
encoding: useText ? "text" : "octet",
|
||||
id: id
|
||||
}, "start"));
|
||||
|
||||
// To avoid a race condition, we don't want to start transmitting to the
|
||||
// server until the server says it is ready.
|
||||
var readyCallback = function (_newName) {
|
||||
newName = _newName;
|
||||
processChunk();
|
||||
};
|
||||
var chunkCallback = function(){
|
||||
if ( !uploadComplete )
|
||||
processChunk();
|
||||
};
|
||||
readyCallbacks[id] = readyCallback;
|
||||
chunkCallbacks[id] = chunkCallback;
|
||||
|
||||
return communicator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function to load the file into memory using the HTML5 FileReader object
|
||||
* and then transmit that file through Socket.IO.
|
||||
*
|
||||
* @param {FileList} files An array of files
|
||||
* @return {void}
|
||||
*/
|
||||
var _load = function (files) {
|
||||
// Iterate through the array of files.
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
// Evaluate each file in a closure, because we will need a new
|
||||
// instance of FileReader for each file.
|
||||
var communicator = _loadOne(files[i]);
|
||||
communicators[communicator.id] = communicator;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function to fetch an HTMLInputElement instance that can be used
|
||||
* during the file selection process.
|
||||
* @return {void}
|
||||
*/
|
||||
var _getInputElement = function () {
|
||||
var inpt = document.getElementById(self.fileInputElementId);
|
||||
if (!inpt) {
|
||||
inpt = document.createElement("input");
|
||||
inpt.setAttribute("type", "file");
|
||||
inpt.setAttribute("id", self.fileInputElementId);
|
||||
inpt.style.display = "none";
|
||||
document.body.appendChild(inpt);
|
||||
}
|
||||
return inpt;
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function to remove an HTMLInputElement created by this instance
|
||||
* of SIOFU.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
var _removeInputElement = function () {
|
||||
var inpt = document.getElementById(self.fileInputElementId);
|
||||
if (inpt) {
|
||||
inpt.parentNode.removeChild(inpt);
|
||||
}
|
||||
};
|
||||
|
||||
var _baseFileSelectCallback = function (files) {
|
||||
if (files.length === 0) return;
|
||||
|
||||
// Ensure existence of meta property on each file
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if(!files[i].meta) files[i].meta = {};
|
||||
}
|
||||
|
||||
// Dispatch the "choose" event
|
||||
var evntResult = _dispatch("choose", {
|
||||
files: files
|
||||
});
|
||||
|
||||
// If the callback didn't return false, continue with the upload
|
||||
if (evntResult) {
|
||||
_load(files);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function that serves as a callback on file input.
|
||||
* @param {Event} event The file input change event
|
||||
* @return {void}
|
||||
*/
|
||||
var _fileSelectCallback = function (event) {
|
||||
var files = event.target.files || event.dataTransfer.files;
|
||||
event.preventDefault();
|
||||
_baseFileSelectCallback(files);
|
||||
|
||||
if (self.resetFileInputs) {
|
||||
try {
|
||||
event.target.value = ""; //for IE11, latest Chrome/Firefox/Opera...
|
||||
} catch(err) {
|
||||
// ignore
|
||||
}
|
||||
if (event.target.value) { //for IE5 ~ IE10
|
||||
var form = document.createElement("form"),
|
||||
parentNode = event.target.parentNode, ref = event.target.nextSibling;
|
||||
form.appendChild(event.target);
|
||||
form.reset();
|
||||
parentNode.insertBefore(event.target, ref);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Submit files at arbitrary time
|
||||
* @param {FileList} files Files received form the input element.
|
||||
* @return {void}
|
||||
*/
|
||||
this.submitFiles = function (files) {
|
||||
if (files) {
|
||||
_baseFileSelectCallback(files);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Use a submitButton to upload files from the field given
|
||||
* @param {HTMLInputElement} submitButton the button that the user has to
|
||||
* click to start the upload
|
||||
* @param {HTMLInputElement} input the field with the data to upload
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
this.listenOnSubmit = function (submitButton, input) {
|
||||
if (!input.files) return;
|
||||
_listenTo(submitButton, "click", function () {
|
||||
_baseFileSelectCallback(input.files);
|
||||
}, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Use a submitButton to upload files from the field given
|
||||
* @param {HTMLInputElement} submitButton the button that the user has to
|
||||
* click to start the upload
|
||||
* @param {Array} array an array of fields with the files to upload
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
this.listenOnArraySubmit = function (submitButton, array) {
|
||||
for (var index in array) {
|
||||
this.listenOnSubmit(submitButton, array[index]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Use a file input to activate this instance of the file uploader.
|
||||
* @param {HTMLInputElement} inpt The input element (e.g., as returned by
|
||||
* document.getElementById("yourId"))
|
||||
* @return {void}
|
||||
*/
|
||||
this.listenOnInput = function (inpt) {
|
||||
if (!inpt.files) return;
|
||||
_listenTo(inpt, "change", _fileSelectCallback, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Accept files dropped on an element and upload them using this instance
|
||||
* of the file uploader.
|
||||
* @param {HTMLELement} div Any HTML element. When the user drags a file
|
||||
* or files onto this element, those files will
|
||||
* be processed by the instance.
|
||||
* @return {void}
|
||||
*/
|
||||
this.listenOnDrop = function (div) {
|
||||
// We need to preventDefault on the dragover event in order for the
|
||||
// drag-and-drop operation to work.
|
||||
_listenTo(div, "dragover", function (event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
_listenTo(div, "drop", _fileSelectCallback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a dialog box for the user to select a file. The file will then
|
||||
* be uploaded using this instance of SocketIOFileUpload.
|
||||
*
|
||||
* This method works in all current browsers except Firefox, though Opera
|
||||
* requires that the input element be visible.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
this.prompt = function () {
|
||||
var inpt = _getInputElement();
|
||||
|
||||
// Listen for the "change" event on the file input element.
|
||||
_listenTo(inpt, "change", _fileSelectCallback, false);
|
||||
|
||||
// Fire a click event on the input element. Firefox does not allow
|
||||
// programatic clicks on input elements, but the other browsers do.
|
||||
// Note that Opera requires that the element be visible when "clicked".
|
||||
var evnt = document.createEvent("MouseEvents");
|
||||
evnt.initMouseEvent("click", true, true, window,
|
||||
0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
inpt.dispatchEvent(evnt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy an instance of Socket.IO file upload (i.e., unbind events and
|
||||
* relieve memory).
|
||||
*
|
||||
* IMPORTANT: To finish the memory relief process, set all external
|
||||
* references to this instance of SIOFU (including the reference used to
|
||||
* call this destroy function) to null.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
this.destroy = function () {
|
||||
_stopListening();
|
||||
_removeInputElement();
|
||||
for (var id in communicators) {
|
||||
if (communicators.hasOwnProperty(id)) {
|
||||
communicators[id].abort = true;
|
||||
}
|
||||
}
|
||||
callbacks = null, uploadedFiles = null, readyCallbacks = null, communicators = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers an event listener. If the callback function returns false,
|
||||
* the file uploader will stop uploading the current file.
|
||||
* @param {string} eventName Type of event for which to listen.
|
||||
* @param {Function} callback Listener function. Will be passed the
|
||||
* event as an argument when the event occurs.
|
||||
* @return {void}
|
||||
*/
|
||||
this.addEventListener = function (eventName, callback) {
|
||||
if (!callbacks[eventName]) callbacks[eventName] = [];
|
||||
callbacks[eventName].push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an event listener.
|
||||
* @param {string} eventName Type of event.
|
||||
* @param {Function} callback Listener function to remove.
|
||||
* @return {boolean} true if callback removed; false otherwise
|
||||
*/
|
||||
this.removeEventListener = function (eventName, callback) {
|
||||
if (!callbacks[eventName]) return false;
|
||||
for (var i = 0; i < callbacks[eventName].length; i++) {
|
||||
if (callbacks[eventName][i] === callback) {
|
||||
callbacks[eventName].splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatches an event into this instance's event model.
|
||||
* @param {Event} evnt The event to dispatch.
|
||||
* @return {boolean} false if any callback returned false; true otherwise
|
||||
*/
|
||||
this.dispatchEvent = function (evnt) {
|
||||
var eventCallbacks = callbacks[evnt.type];
|
||||
if (!eventCallbacks) return true;
|
||||
var retVal = true;
|
||||
for (var i = 0; i < eventCallbacks.length; i++) {
|
||||
var callbackResult = eventCallbacks[i](evnt);
|
||||
if (callbackResult === false) {
|
||||
retVal = false;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
// OTHER LIBRARIES
|
||||
/*
|
||||
* base64-arraybuffer
|
||||
* https://github.com/niklasvh/base64-arraybuffer
|
||||
*
|
||||
* Copyright (c) 2012 Niklas von Hertzen
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* Adapted for SocketIOFileUpload.
|
||||
*/
|
||||
var _uint8ArrayToBase64 = function (bytes) {
|
||||
var i, len = bytes.buffer.byteLength, base64 = "",
|
||||
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
for (i = 0; i < len; i += 3) {
|
||||
base64 += chars[bytes[i] >> 2];
|
||||
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
|
||||
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
|
||||
base64 += chars[bytes[i + 2] & 63];
|
||||
}
|
||||
|
||||
if ((len % 3) === 2) {
|
||||
base64 = base64.substring(0, base64.length - 1) + "=";
|
||||
}
|
||||
else if (len % 3 === 1) {
|
||||
base64 = base64.substring(0, base64.length - 2) + "==";
|
||||
}
|
||||
|
||||
return base64;
|
||||
};
|
||||
// END OTHER LIBRARIES
|
||||
var _chunckCallback = function(data) {
|
||||
if ( chunkCallbacks[data.id] )
|
||||
chunkCallbacks[data.id]();
|
||||
};
|
||||
|
||||
var _readyCallback = function (data) {
|
||||
if (readyCallbacks[data.id])
|
||||
readyCallbacks[data.id](data.name);
|
||||
};
|
||||
|
||||
var _completCallback = function (data) {
|
||||
if (uploadedFiles[data.id]) {
|
||||
_dispatch("complete", {
|
||||
file: uploadedFiles[data.id],
|
||||
detail: data.detail,
|
||||
success: data.success
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var _errorCallback = function (data) {
|
||||
if ( uploadedFiles[data.id] ) {
|
||||
_dispatch("error", {
|
||||
file: uploadedFiles[data.id],
|
||||
message: data.message,
|
||||
code: 0
|
||||
});
|
||||
if (communicators) communicators[data.id].abort = true;
|
||||
}
|
||||
};
|
||||
|
||||
// CONSTRUCTOR: Listen to the "complete", "ready", and "error" messages
|
||||
// on the socket.
|
||||
if (_isWrapDataWellConfigured() && self.wrapData) {
|
||||
var mapActionToCallback = {
|
||||
chunk: _chunckCallback,
|
||||
ready: _readyCallback,
|
||||
complete: _completCallback,
|
||||
error: _errorCallback
|
||||
};
|
||||
|
||||
_listenTo(socket, _getTopicName(), function (message) {
|
||||
if (typeof message !== "object") {
|
||||
console.log("SocketIOFileUploadClient Error: You choose to wrap your data so the message from the server need to be an object"); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
var actionKey = self.wrapData.unwrapKey && typeof self.wrapData.unwrapKey.action === "string" ? self.wrapData.unwrapKey.action : "action";
|
||||
var messageKey = self.wrapData.unwrapKey && typeof self.wrapData.unwrapKey.message === "string" ? self.wrapData.unwrapKey.message : "message";
|
||||
|
||||
var action = message[actionKey];
|
||||
var data = message[messageKey];
|
||||
if (!action || !data || !mapActionToCallback[action]) {
|
||||
console.log("SocketIOFileUploadClient Error: You choose to wrap your data but the message from the server is wrong configured. Check the message and your wrapData option"); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
mapActionToCallback[action](data);
|
||||
});
|
||||
} else {
|
||||
_listenTo(socket, _getTopicName("_chunk"), _chunckCallback);
|
||||
_listenTo(socket, _getTopicName("_ready"), _readyCallback);
|
||||
_listenTo(socket, _getTopicName("_complete"), _completCallback);
|
||||
_listenTo(socket, _getTopicName("_error"), _errorCallback);
|
||||
}
|
||||
|
||||
if (this.exposePrivateFunction) {
|
||||
this.chunckCallback = _chunckCallback;
|
||||
this.readyCallback = _readyCallback;
|
||||
this.completCallback = _completCallback;
|
||||
this.errorCallback = _errorCallback;
|
||||
}
|
||||
};
|
||||
}));
|
||||
4747
app/public/js/socket.io.js
Normal file
4747
app/public/js/socket.io.js
Normal file
File diff suppressed because it is too large
Load Diff
1
app/public/script.js
Normal file
1
app/public/script.js
Normal file
@@ -0,0 +1 @@
|
||||
// script.js
|
||||
Reference in New Issue
Block a user