Move the file upload code in a separate file

pull/1/head
yflory 8 years ago
parent a58162a617
commit 803bad1421

@ -0,0 +1,260 @@
define([
'jquery',
'/file/file-crypto.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto) {
var Nacl = window.nacl;
var module = {};
module.create = function (common) {
var File = {};
var Messages = common.Messages;
var queue = File.queue = {
queue: [],
inProgress: false
};
var uid = function () {
return 'file-' + String(Math.random()).substring(2);
};
var $table = File.$table = $('<table>', { id: 'uploadStatus' });
var $thead = $('<tr>').appendTo($table);
$('<td>').text(Messages.upload_name).appendTo($thead);
$('<td>').text(Messages.upload_size).appendTo($thead);
$('<td>').text(Messages.upload_progress).appendTo($thead);
$('<td>').text(Messages.cancel).appendTo($thead);
var createTableContainer = function ($body) {
File.$container = $('<div>', { id: 'uploadStatusContainer' }).append($table).appendTo($body);
return File.$container;
};
var upload = function (blob, metadata, id) {
if (queue.inProgress) { return; }
queue.inProgress = true;
var $row = $table.find('tr[id="'+id+'"]');
$row.find('.upCancel').html('-');
var $pv = $row.find('.progressValue');
var $pb = $row.find('.progressContainer');
var $link = $row.find('.upLink');
var updateProgress = function (progressValue) {
$pv.text(Math.round(progressValue*100)/100 + '%');
$pb.css({
width: (progressValue/100)*188+'px'
});
};
var u8 = new Uint8Array(blob);
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata);
var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box);
Cryptpad.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
console.log(box);
cb(e, msg);
});
};
var actual = 0;
var again = function (err, box) {
if (err) { throw new Error(err); }
if (box) {
actual += box.length;
var progressValue = (actual / estimate * 100);
updateProgress(progressValue);
return void sendChunk(box, function (e) {
if (e) { return console.error(e); }
next(again);
});
}
if (actual !== estimate) {
console.error('Estimated size does not match actual size');
}
// if not box then done
Cryptpad.uploadComplete(function (e, id) {
if (e) { return void console.error(e); }
var uri = ['', 'blob', id.slice(0,2), id].join('/');
console.log("encrypted blob is now available as %s", uri);
var b64Key = Nacl.util.encodeBase64(key);
var hash = Cryptpad.getFileHashFromKeys(id, b64Key);
var href = '/file/#' + hash;
$link.attr('href', href)
.click(function (e) {
e.preventDefault();
window.open($link.attr('href'), '_blank');
});
// TODO add button to table which copies link to clipboard?
//APP.toolbar.addElement(['fileshare'], {});
var title = document.title = metadata.name;
myFile = blob;
myDataType = metadata.type;
Cryptpad.renamePad(title || "", href, function (err) {
if (err) { console.error(err); } // TODO
console.log(title);
Cryptpad.log(Messages._getKey('upload_success', [title]));
queue.inProgress = false;
queue.next();
})
//Title.updateTitle(title || "", href);
//APP.toolbar.title.show();
});
};
Cryptpad.uploadStatus(estimate, function (e, pending) {
if (e) {
queue.inProgress = false;
queue.next();
if (e === 'TOO_LARGE') {
// TODO update table to say too big?
return void Cryptpad.alert(Messages.upload_tooLarge);
}
if (e === 'NOT_ENOUGH_SPACE') {
// TODO update table to say not enough space?
return void Cryptpad.alert(Messages.upload_notEnoughSpace);
}
console.error(e);
return void Cryptpad.alert(Messages.upload_serverError);
}
if (pending) {
// TODO keep this message in case of pending files in another window?
return void Cryptpad.confirm(Messages.upload_uploadPending, function (yes) {
if (!yes) { return; }
Cryptpad.uploadCancel(function (e, res) {
if (e) {
return void console.error(e);
}
console.log(res);
next(again);
});
});
}
next(again);
});
};
var prettySize = function (bytes) {
var kB = Cryptpad.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = Cryptpad.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
queue.next = function () {
if (queue.queue.length === 0) { return; }
if (queue.inProgress) { return; }
var file = queue.queue.shift();
upload(file.blob, file.metadata, file.id);
};
queue.push = function (obj) {
var id = uid();
obj.id = id;
queue.queue.push(obj);
$table.show();
var estimate = FileCrypto.computeEncryptedSize(obj.blob.byteLength, obj.metadata);
var $progressBar = $('<div>', {'class':'progressContainer'});
var $progressValue = $('<span>', {'class':'progressValue'}).text(Messages.upload_pending);
var $tr = $('<tr>', {id: id}).appendTo($table);
var $cancel = $('<span>', {'class': 'cancel fa fa-times'}).click(function () {
queue.queue = queue.queue.filter(function (el) { return el.id !== id; });
$cancel.remove();
$tr.find('.upCancel').text('-');
$tr.find('.progressValue').text(Messages.upload_cancelled);
});
var $link = $('<a>', {
'class': 'upLink',
'rel': 'noopener noreferrer'
}).text(obj.metadata.name);
$('<td>').append($link).appendTo($tr);
$('<td>').text(prettySize(estimate)).appendTo($tr);
$('<td>', {'class': 'upProgress'}).append($progressBar).append($progressValue).appendTo($tr);
$('<td>', {'class': 'upCancel'}).append($cancel).appendTo($tr);
queue.next();
};
var handleFile = File.handleFile = function (file) {
console.log(file);
var reader = new FileReader();
reader.onloadend = function () {
queue.push({
blob: this.result,
metadata: {
name: file.name,
type: file.type,
}
});
};
reader.readAsArrayBuffer(file);
};
var createAreaHandlers = File.createDropArea = function ($area, $hoverArea, todo) {
var counter = 0;
if (!$hoverArea) { $hoverArea = $area; }
$hoverArea
.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$label.addClass('hovering');
})
.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
$label.removeClass('hovering');
}
});
$area
.on('drag dragstart dragend dragover drop dragenter dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
})
.on('drop', function (e) {
e.stopPropagation();
var dropped = e.originalEvent.dataTransfer.files;
counter = 0;
$label.removeClass('hovering');
Array.prototype.slice.call(dropped).forEach(function (d) {
todo(d);
});
});
};
File.createUploader = function ($area, $hover, $body) {
createAreaHandlers($area, null, handleFile);
var $c = createTableContainer($body);
};
return File;
};
return module;
});

@ -43,11 +43,12 @@ define(function () {
onLocal(); onLocal();
}; };
exp.updateTitle = function (newTitle) { // update title: href is optional; if not specified, we use window.location.href
exp.updateTitle = function (newTitle, href) {
if (newTitle === exp.title) { return; } if (newTitle === exp.title) { return; }
// Change the title now, and set it back to the old value if there is an error // Change the title now, and set it back to the old value if there is an error
var oldTitle = exp.title; var oldTitle = exp.title;
Cryptpad.renamePad(newTitle, function (err, data) { Cryptpad.renamePad(newTitle, href, function (err, data) {
if (err) { if (err) {
console.log("Couldn't set pad title"); console.log("Couldn't set pad title");
console.error(err); console.error(err);

@ -11,11 +11,13 @@ define([
'/common/common-title.js', '/common/common-title.js',
'/common/common-metadata.js', '/common/common-metadata.js',
'/common/common-codemirror.js', '/common/common-codemirror.js',
'/common/common-file.js',
'/common/clipboard.js', '/common/clipboard.js',
'/common/pinpad.js', '/common/pinpad.js',
'/customize/application_config.js' '/customize/application_config.js'
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, CodeMirror, Clipboard, Pinpad, AppConfig) { ], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
CodeMirror, Files, Clipboard, Pinpad, AppConfig) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata any particular pad type. This includes functions for committing metadata
@ -114,6 +116,9 @@ define([
// CodeMirror // CodeMirror
common.createCodemirror = CodeMirror.create; common.createCodemirror = CodeMirror.create;
// Files
common.createFileManager = function () { return Files.create(common); };
// History // History
common.getHistory = function (config) { return History.create(common, config); }; common.getHistory = function (config) { return History.create(common, config); };
@ -520,8 +525,8 @@ define([
cb ("store.forgetPad is not a function"); cb ("store.forgetPad is not a function");
}; };
common.setPadTitle = function (name, cb) { common.setPadTitle = function (name, padHref, cb) {
var href = window.location.href; var href = padHref || window.location.href;
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
if (!parsed.hash) { return; } if (!parsed.hash) { return; }
href = getRelativeHref(href); href = getRelativeHref(href);
@ -621,15 +626,15 @@ define([
/* /*
* Buttons * Buttons
*/ */
common.renamePad = function (title, callback) { common.renamePad = function (title, href, callback) {
if (title === null) { return; } if (title === null) { return; }
if (title.trim() === "") { if (title.trim() === "") {
var parsed = parsePadUrl(window.location.href); var parsed = parsePadUrl(href || window.location.href);
title = getDefaultName(parsed); title = getDefaultName(parsed);
} }
common.setPadTitle(title, function (err) { common.setPadTitle(title, href, function (err) {
if (err) { if (err) {
console.log("unable to set pad title"); console.log("unable to set pad title");
console.log(err); console.log(err);

@ -438,7 +438,7 @@ define([
if (name === "") { if (name === "") {
name = $input.attr('placeholder'); name = $input.attr('placeholder');
} }
Cryptpad.renamePad(name, function (err, newtitle) { Cryptpad.renamePad(name, null, function (err, newtitle) {
if (err) { return; } if (err) { return; }
$text.text(newtitle); $text.text(newtitle);
callback(null, newtitle); callback(null, newtitle);

@ -77,7 +77,7 @@ body {
z-index: 10000; z-index: 10000;
display: block; display: block;
} }
#status { #uploadStatus {
display: none; display: none;
width: 80vw; width: 80vw;
margin-top: 50px; margin-top: 50px;
@ -85,24 +85,24 @@ body {
border: 1px solid black; border: 1px solid black;
border-collapse: collapse; border-collapse: collapse;
} }
#status tr:nth-child(1) { #uploadStatus tr:nth-child(1) {
background-color: #ccc; background-color: #ccc;
border: 1px solid #999; border: 1px solid #999;
} }
#status tr:nth-child(1) td { #uploadStatus tr:nth-child(1) td {
text-align: center; text-align: center;
} }
#status td { #uploadStatus td {
border-left: 1px solid #BBB; border-left: 1px solid #BBB;
border-right: 1px solid #BBB; border-right: 1px solid #BBB;
padding: 0 10px; padding: 0 10px;
} }
#status .upProgress { #uploadStatus .upProgress {
width: 200px; width: 200px;
position: relative; position: relative;
text-align: center; text-align: center;
} }
#status .progressContainer { #uploadStatus .progressContainer {
position: absolute; position: absolute;
width: 0px; width: 0px;
left: 5px; left: 5px;
@ -110,9 +110,9 @@ body {
bottom: 1px; bottom: 1px;
background-color: rgba(0, 0, 255, 0.3); background-color: rgba(0, 0, 255, 0.3);
} }
#status .upCancel { #uploadStatus .upCancel {
text-align: center; text-align: center;
} }
#status .fa.cancel { #uploadStatus .fa.cancel {
color: #ff0073; color: #ff0073;
} }

@ -87,7 +87,7 @@ html, body {
display: block; display: block;
} }
#status { #uploadStatus {
display: none; display: none;
width: 80vw; width: 80vw;
margin-top: 50px; margin-top: 50px;

@ -21,14 +21,14 @@
data-localization="download_button"></label> data-localization="download_button"></label>
<span class="block" id="progress"></span> <span class="block" id="progress"></span>
</div> </div>
<table id="status" style="display: none;"> <!--<table id="status" style="display: none;">
<tr> <tr>
<td data-localization="upload_name">File name</td> <td data-localization="upload_name">File name</td>
<td data-localization="upload_size">Size</td> <td data-localization="upload_size">Size</td>
<td data-localization="upload_progress">Progress</td> <td data-localization="upload_progress">Progress</td>
<td data-localization="cancel">Cancel</td> <td data-localization="cancel">Cancel</td>
</tr> </tr>
</table> </table>-->
<div id="feedback" class="block hidden"> <div id="feedback" class="block hidden">
</div> </div>
</body> </body>

@ -17,7 +17,7 @@ define([
var APP = {}; var APP = {};
$(function () { $(function () {
// TODO race condition with contents() here
var ifrw = $('#pad-iframe')[0].contentWindow; var ifrw = $('#pad-iframe')[0].contentWindow;
var $iframe = $('#pad-iframe').contents(); var $iframe = $('#pad-iframe').contents();
var $form = $iframe.find('#upload-form'); var $form = $iframe.find('#upload-form');
@ -25,9 +25,10 @@ define([
var $label = $form.find('label'); var $label = $form.find('label');
var $table = $iframe.find('#status'); var $table = $iframe.find('#status');
var $progress = $iframe.find('#progress'); var $progress = $iframe.find('#progress');
var $body = $iframe.find('body');
$iframe.find('body').on('dragover', function (e) { e.preventDefault(); }); $body.on('dragover', function (e) { e.preventDefault(); });
$iframe.find('body').on('drop', function (e) { e.preventDefault(); }); $body.on('drop', function (e) { e.preventDefault(); });
Cryptpad.addLoadingScreen(); Cryptpad.addLoadingScreen();
@ -35,7 +36,7 @@ define([
var myFile; var myFile;
var myDataType; var myDataType;
/*
var queue = { var queue = {
queue: [], queue: [],
inProgress: false inProgress: false
@ -206,7 +207,7 @@ define([
queue.next(); queue.next();
}; };
*/
var uploadMode = false; var uploadMode = false;
var andThen = function () { var andThen = function () {
@ -329,7 +330,7 @@ define([
display: 'block', display: 'block',
}); });
var handleFile = function (file) { /*var handleFile = function (file) {
console.log(file); console.log(file);
var reader = new FileReader(); var reader = new FileReader();
reader.onloadend = function () { reader.onloadend = function () {
@ -342,45 +343,17 @@ define([
}); });
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
}; };*/
var FM = Cryptpad.createFileManager();
$form.find("#file").on('change', function (e) { $form.find("#file").on('change', function (e) {
var file = e.target.files[0]; var file = e.target.files[0];
handleFile(file); FM.handleFile(file);
}); });
var counter = 0; //FM.createDropArea($form, $label, handleFile);
$label FM.createUploader($form, $label, $body);
.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$label.addClass('hovering');
})
.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
$label.removeClass('hovering');
}
});
$form
.on('drag dragstart dragend dragover drop dragenter dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
})
.on('drop', function (e) {
e.stopPropagation();
var dropped = e.originalEvent.dataTransfer.files;
counter = 0;
$label.removeClass('hovering');
Array.prototype.slice.call(dropped).forEach(function (d) {
handleFile(d);
});
});
// we're in upload mode // we're in upload mode
Cryptpad.removeLoadingScreen(); Cryptpad.removeLoadingScreen();

Loading…
Cancel
Save