var uploadLog = function(message) { var uploadConsole = document.getElementById("upload-console"); if(uploadConsole.childNodes.length > 15) { uploadConsole.removeChild(uploadConsole.childNodes[0]); } var textContainer = document.createElement("div"); var textNode = document.createTextNode(message); textContainer.appendChild(textNode); uploadConsole.appendChild(textContainer); }; var ResumeFileUpload = function(fileToUpload, retries) { this.finished = false; var self = this; this.start = function() { var file = fileManager.lookup(fileToUpload.filepath); if(file != null) { this.getAccessToken(file); } else { console.error("file not in expected upload list: " + fileToUpload.filepath); fileManager.batch.pool.fileFinished(fileToUpload.upload_id); } }; this.getAccessToken = function(file) { var data = {}; data.filePath = file.webkitRelativePath; data.fileType = file.type; data.project_id = fileManager.projectId; data.resume = true; var promise = ajaxPost("/api/putfileaccesstoken", data); promise.success = function(event) { self.readFileContents(file, event); }; promise.failure = function(event) { console.error("failure getting the access token: " + fileToUpload.filepath); fileManager.batch.pool.fileFailed(fileToUpload, ++retries); }; }; this.readFileContents = function(file, auth) { fileReader = new FileReader(); fileReader.onload = function(event) { self.putFile(auth.url, event.target.result, auth.headers); }; fileReader.readAsArrayBuffer(file); }; this.putFile = function(url, fileData, headers) { var promise = ajaxPut(url, fileData, headers); promise.success = function(event) { self.confirmUpload(); }; promise.failure = function(event) { console.error("failure uploading file: " + fileToUpload.filepath); fileManager.batch.pool.fileFailed(fileToUpload, ++retries); } }; this.confirmUpload = function() { var data = {"project_id": fileManager.projectId, "upload_id": fileToUpload.upload_id}; var promise = ajaxPost('/api/confirmupload2', data); promise.success = function(event) { self.finished = true; fileManager.batch.pool.fileFinished(fileToUpload.upload_id); uploadLog("Uploaded file "+ fileToUpload.filepath); }; promise.failure = function(event) { console.error("unable to mark file as completed: "+ fileToUpload.filepath); fileManager.batch.pool.fileFailed(fileToUpload, ++retries); }; }; }; var ResumePool = function() { this.slots = []; var maxSlots = 10; var maxRetries = 4; var self = this; this.start = function() { window.requestAnimationFrame(function() { for(var i = 0; i < maxSlots; i++) { self.processNextFile(); } }); }; this.processNextFile = function() { var activeFile = fileManager.batch.resumeFiles.pop(); if(activeFile != null) { self.slots.push(activeFile.upload_id); var uploader = new ResumeFileUpload(activeFile, 0); uploader.start(); } if(self.slots.length <= 0) { fileManager.batch.poolFinished(); } }; this.fileFinished = function(uploadId) { var foundUpload = self.slots.indexOf(uploadId); if(foundUpload > -1) { self.slots.splice(foundUpload, 1); document.getElementById('progress-number').innerHTML = parseInt(document.getElementById('progress-number').innerHTML) - 1; } window.requestAnimationFrame(function() { self.processNextFile(); }); }; this.fileFailed = function(fileToUpload, retries) { if(retries >= maxRetries) { this.fileFinished(fileToUpload.upload_id); return; } console.warn("retrying file attempt "+ retries +": " + fileToUpload.filepath); var uploader = new ResumeFileUpload(fileToUpload, retries); uploader.start(); }; }; var ResumeBatch = function() { this.resumeFiles = []; this.pool = new ResumePool(); var offset = 0; var limit = 50; var remaining = 0; var self = this; this.start = function() { var fileRestoreData = {}; fileRestoreData.project_id = fileManager.projectId; fileRestoreData.limit = limit; var fileRestorePromise = ajaxPost('/api/getfilestoresume', fileRestoreData); fileRestorePromise.success = function(event) { self.resumeFiles = event.files; console.log("Remaining: " + event.total); document.getElementById('progress-number').innerHTML = event.total; if(0 < self.resumeFiles.length) { self.pool.start(); } else { fileManager.batchFinished(); } }; fileRestorePromise.failure = function(event) { console.error("unable to retrieve list of resume files"); }; }; this.poolFinished = function() { offset += limit; uploadLog("--- Batch Finished ("+ offset +") ---"); this.start(); }; }; var ResumeFileManager = function() { this.fileDictionary = {}; this.fileList = {}; this.projectId = 0; this.batch = new ResumeBatch(); this.process = function() { uploadLog("--- Processing Browser Files ---"); this.fileList = document.getElementById('dir-input').files; for(var i = 0; i < this.fileList.length; i++){ this.fileDictionary[this.fileList[i].webkitRelativePath] = i; } document.getElementById('progress-number').innerHTML = this.fileList.length; uploadLog("--- Ready to Begin ---"); }; this.resumeUpload = function(projectId) { this.projectId = projectId; this.batch.start(); }; this.lookup = function(filepath) { return this.fileList[this.fileDictionary[filepath]]; } this.batchFinished = function() { uploadLog("--- Upload Complete! ---"); console.log("Upload complete!"); }; }; var fileManager = new ResumeFileManager();