Merge branch 'cacheRT' into staging
commit
7c187f92cb
|
@ -213,3 +213,54 @@ media-tag * {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
media-tag button.btn {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 6px;
|
||||
min-height: 36px;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
transition: none;
|
||||
color: #3F4141;
|
||||
border: 1px solid #3F4141;
|
||||
max-width: 250px;
|
||||
}
|
||||
media-tag button.mediatag-download-btn {
|
||||
flex-flow: column;
|
||||
min-height: 38px;
|
||||
justify-content: center;
|
||||
}
|
||||
media-tag button.mediatag-download-btn > span {
|
||||
display: flex;
|
||||
line-height: 1.5;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
media-tag button.mediatag-download-btn * {
|
||||
width: auto;
|
||||
}
|
||||
media-tag button.mediatag-download-btn > span.mediatag-download-name b {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
media-tag button.btn:hover, media-tag button.btn:active, media-tag button.btn:focus {
|
||||
background-color: #ccc;
|
||||
}
|
||||
media-tag button.btn b {
|
||||
margin-left: 5px;
|
||||
}
|
||||
media-tag button.btn .fa {
|
||||
display: inline;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ p.cp-password-info{
|
|||
animation-timing-function: cubic-bezier(.6,0.15,0.4,0.85);
|
||||
}
|
||||
|
||||
button.primary{
|
||||
button:not(.btn).primary{
|
||||
border: 1px solid #4591c4;
|
||||
padding: 8px 12px;
|
||||
text-transform: uppercase;
|
||||
|
@ -250,7 +250,7 @@ button.primary{
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
button.primary:hover{
|
||||
button:not(.btn).primary:hover{
|
||||
background-color: rgb(52, 118, 162);
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ button.primary:hover{
|
|||
var built = false;
|
||||
|
||||
var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end'];
|
||||
var current;
|
||||
var current, progress;
|
||||
var makeList = function (data) {
|
||||
var c = types.indexOf(data.type);
|
||||
current = c;
|
||||
|
@ -295,7 +295,7 @@ button.primary:hover{
|
|||
};
|
||||
var list = '<ul>';
|
||||
types.forEach(function (el, i) {
|
||||
if (i >= 6) { return; }
|
||||
if (el === "end") { return; }
|
||||
list += getLi(i);
|
||||
});
|
||||
list += '</ul>';
|
||||
|
@ -303,7 +303,7 @@ button.primary:hover{
|
|||
};
|
||||
var makeBar = function (data) {
|
||||
var c = types.indexOf(data.type);
|
||||
var l = types.length;
|
||||
var l = types.length - 1; // don't count "end" as a type
|
||||
var progress = Math.min(data.progress, 100);
|
||||
var p = (progress / l) + (100 * c / l);
|
||||
var bar = '<div class="cp-loading-progress-bar">'+
|
||||
|
@ -315,14 +315,22 @@ button.primary:hover{
|
|||
var hasErrored = false;
|
||||
var updateLoadingProgress = function (data) {
|
||||
if (!built || !data) { return; }
|
||||
|
||||
// Make sure progress doesn't go backward
|
||||
var c = types.indexOf(data.type);
|
||||
if (c < current) { return console.error(data); }
|
||||
if (c === current && progress > data.progress) { return console.error(data); }
|
||||
progress = data.progress;
|
||||
|
||||
try {
|
||||
document.querySelector('.cp-loading-spinner-container').style.display = 'none';
|
||||
document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data);
|
||||
document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data);
|
||||
var el1 = document.querySelector('.cp-loading-spinner-container');
|
||||
if (el1) { el1.style.display = 'none'; }
|
||||
var el2 = document.querySelector('.cp-loading-progress-list');
|
||||
if (el2) { el2.innerHTML = makeList(data); }
|
||||
var el3 = document.querySelector('.cp-loading-progress-container');
|
||||
if (el3) { el3.innerHTML = makeBar(data); }
|
||||
} catch (e) {
|
||||
if (!hasErrored) { console.error(e); }
|
||||
//if (!hasErrored) { console.error(e); }
|
||||
}
|
||||
};
|
||||
window.CryptPad_updateLoadingProgress = updateLoadingProgress;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
right: 10vw;
|
||||
bottom: 10vh;
|
||||
box-sizing: border-box;
|
||||
z-index: 100000; //Z file upload table container
|
||||
z-index: 100001; //Z file upload table container: just above the file picker
|
||||
display: none;
|
||||
color: darken(@colortheme_drive-bg, 10%);
|
||||
max-height: 180px;
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
@import (reference) "./variables.less";
|
||||
|
||||
.forms_main() {
|
||||
--LessLoader_require: LessLoader_currentFile();
|
||||
}
|
||||
|
||||
& {
|
||||
@alertify-fore: @colortheme_modal-fg;
|
||||
@alertify-btn-fg: @alertify-fore;
|
||||
@alertify-light-bg: fade(@alertify-fore, 25%);
|
||||
|
@ -124,6 +128,14 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.btn-default {
|
||||
border-color: @cryptpad_text_col;
|
||||
color: @cryptpad_text_col;
|
||||
&:hover, &:active, &:focus {
|
||||
background-color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
&.danger, &.btn-danger {
|
||||
background-color: @colortheme_alertify-red;
|
||||
border-color: @colortheme_alertify-red-border;
|
||||
|
|
|
@ -64,6 +64,54 @@
|
|||
}
|
||||
}
|
||||
|
||||
.mediatag_cryptpad() {
|
||||
media-tag {
|
||||
&:empty {
|
||||
display: none !important;
|
||||
}
|
||||
cursor: pointer;
|
||||
* {
|
||||
max-width: 100%;
|
||||
}
|
||||
iframe[src$=".pdf"] {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
max-height: 90vh;
|
||||
}
|
||||
button.mediatag-download-btn {
|
||||
flex-flow: column;
|
||||
& > span {
|
||||
display: flex;
|
||||
line-height: 1.5;
|
||||
align-items: center;
|
||||
&.mediatag-download-name b {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
button.btn-default {
|
||||
display: inline-flex;
|
||||
max-width: 250px;
|
||||
min-height: 38px;
|
||||
justify-content: center;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
b {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
media-tag:empty {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: inline-block;
|
||||
border: 1px solid #BBB;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown_cryptpad() {
|
||||
word-wrap: break-word;
|
||||
|
||||
|
@ -84,23 +132,8 @@
|
|||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
media-tag {
|
||||
cursor: pointer;
|
||||
* {
|
||||
max-width: 100%;
|
||||
}
|
||||
iframe[src$=".pdf"] {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
max-height: 90vh;
|
||||
}
|
||||
}
|
||||
media-tag:empty {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: inline-block;
|
||||
border: 1px solid #BBB;
|
||||
}
|
||||
|
||||
.mediatag_cryptpad();
|
||||
|
||||
pre.markmap {
|
||||
border: 1px solid #ddd;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@import (reference) "./colortheme-all.less";
|
||||
@import (reference) "./variables.less";
|
||||
@import (reference) "./browser.less";
|
||||
@import (reference) "./markdown.less";
|
||||
|
||||
.modals-ui-elements_main() {
|
||||
--LessLoader_require: LessLoader_currentFile();
|
||||
|
@ -214,6 +215,7 @@
|
|||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
.mediatag_cryptpad();
|
||||
media-tag {
|
||||
& > * {
|
||||
max-width: 100%;
|
||||
|
|
|
@ -118,7 +118,7 @@
|
|||
//border-radius: 0 0.25em 0.25em 0;
|
||||
//border: 1px solid #adadad;
|
||||
border-left: 0px;
|
||||
height: @variables_input-height;
|
||||
height: 40px;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,8 +177,8 @@ server {
|
|||
add_header Cache-Control max-age=31536000;
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length';
|
||||
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length';
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
|
|
|
@ -419,9 +419,11 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => {
|
|||
// fall through to the next block if the offset of the hash in question is not in memory
|
||||
if (lastKnownHash && typeof(lkh) !== "number") { return; }
|
||||
|
||||
// If we have a lastKnownHash or we didn't ask for one, we don't need the next blocks
|
||||
waitFor.abort();
|
||||
|
||||
// Since last 2 checkpoints
|
||||
if (!lastKnownHash) {
|
||||
waitFor.abort();
|
||||
// Less than 2 checkpoints in the history: return everything
|
||||
if (index.cpIndex.length < 2) { return void cb(null, 0); }
|
||||
// Otherwise return the second last checkpoint's index
|
||||
|
@ -436,7 +438,16 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => {
|
|||
to reconcile their differences. */
|
||||
}
|
||||
|
||||
offset = lkh;
|
||||
// If our lastKnownHash is older than the 2nd to last checkpoint,
|
||||
// only send the last 2 checkpoints and ignore "lkh"
|
||||
// XXX XXX this is probably wrong! ChainPad may not accept checkpoints that are not connected to root
|
||||
// XXX We probably need to send an EUNKNOWN here so that the client can recreate a new chainpad
|
||||
/*if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) {
|
||||
return void cb(null, index.cpIndex[0].offset);
|
||||
}*/
|
||||
|
||||
// Otherwise use our lastKnownHash
|
||||
cb(null, lkh);
|
||||
}));
|
||||
}).nThen((w) => {
|
||||
// skip past this block if the offset is anything other than -1
|
||||
|
|
14
server.js
14
server.js
|
@ -136,6 +136,20 @@ app.head(/^\/common\/feedback\.html/, function (req, res, next) {
|
|||
});
|
||||
}());
|
||||
|
||||
app.use('/blob', function (req, res, next) {
|
||||
if (req.method === 'HEAD') {
|
||||
Express.static(Path.join(__dirname, (config.blobPath || './blob')), {
|
||||
setHeaders: function (res, path, stat) {
|
||||
res.set('Access-Control-Allow-Origin', '*');
|
||||
res.set('Access-Control-Allow-Headers', 'Content-Length');
|
||||
res.set('Access-Control-Expose-Headers', 'Content-Length');
|
||||
}
|
||||
})(req, res, next);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
if (req.method === 'OPTIONS' && /\/blob\//.test(req.url)) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
|
|
|
@ -65,12 +65,13 @@ define([
|
|||
switch (e.which) {
|
||||
case 27: // cancel
|
||||
if (typeof(no) === 'function') { no(e); }
|
||||
$(el || window).off('keydown', handler);
|
||||
break;
|
||||
case 13: // enter
|
||||
if (typeof(yes) === 'function') { yes(e); }
|
||||
$(el || window).off('keydown', handler);
|
||||
break;
|
||||
}
|
||||
$(el || window).off('keydown', handler);
|
||||
};
|
||||
|
||||
$(el || window).keydown(handler);
|
||||
|
|
|
@ -3,9 +3,9 @@ define([
|
|||
'/common/common-util.js',
|
||||
'/common/visible.js',
|
||||
'/common/common-hash.js',
|
||||
'/file/file-crypto.js',
|
||||
'/common/media-tag.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function ($, Util, Visible, Hash, FileCrypto) {
|
||||
], function ($, Util, Visible, Hash, MediaTag) {
|
||||
var Nacl = window.nacl;
|
||||
var Thumb = {
|
||||
dimension: 100,
|
||||
|
@ -314,7 +314,7 @@ define([
|
|||
var hexFileName = secret.channel;
|
||||
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||
MediaTag.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||
if (e) {
|
||||
if (e === 'XHR_ERROR') { return; }
|
||||
return console.error(e);
|
||||
|
|
|
@ -274,28 +274,73 @@
|
|||
|
||||
|
||||
// given a path, asynchronously return an arraybuffer
|
||||
Util.fetch = function (src, cb, progress) {
|
||||
var CB = Util.once(cb);
|
||||
var getCacheKey = function (src) {
|
||||
var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes
|
||||
var idx = _src.lastIndexOf('/');
|
||||
var cacheKey = _src.slice(idx+1);
|
||||
if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; }
|
||||
return cacheKey;
|
||||
};
|
||||
Util.fetch = function (src, cb, progress, cache) {
|
||||
var CB = Util.once(Util.mkAsync(cb));
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", src, true);
|
||||
if (progress) {
|
||||
xhr.addEventListener("progress", function (evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = evt.loaded / evt.total;
|
||||
progress(percentComplete);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onerror = function (err) { CB(err); };
|
||||
xhr.onload = function () {
|
||||
if (/^4/.test(''+this.status)) {
|
||||
return CB('XHR_ERROR');
|
||||
}
|
||||
return void CB(void 0, new Uint8Array(xhr.response));
|
||||
var cacheKey = getCacheKey(src);
|
||||
var getBlobCache = function (id, cb) {
|
||||
if (!cache || typeof(cache.getBlobCache) !== "function") { return void cb('EINVAL'); }
|
||||
cache.getBlobCache(id, cb);
|
||||
};
|
||||
var setBlobCache = function (id, u8, cb) {
|
||||
if (!cache || typeof(cache.setBlobCache) !== "function") { return void cb('EINVAL'); }
|
||||
cache.setBlobCache(id, u8, cb);
|
||||
};
|
||||
|
||||
var xhr;
|
||||
|
||||
var fetch = function () {
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", src, true);
|
||||
if (progress) {
|
||||
xhr.addEventListener("progress", function (evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = evt.loaded / evt.total;
|
||||
progress(percentComplete);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onerror = function (err) { CB(err); };
|
||||
xhr.onload = function () {
|
||||
if (/^4/.test(''+this.status)) {
|
||||
return CB('XHR_ERROR');
|
||||
}
|
||||
|
||||
var arrayBuffer = xhr.response;
|
||||
if (arrayBuffer) {
|
||||
var u8 = new Uint8Array(arrayBuffer);
|
||||
if (cacheKey) {
|
||||
return void setBlobCache(cacheKey, u8, function () {
|
||||
CB(null, u8);
|
||||
});
|
||||
}
|
||||
return void CB(void 0, u8);
|
||||
}
|
||||
CB('ENOENT');
|
||||
};
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
if (!cacheKey) { return void fetch(); }
|
||||
|
||||
getBlobCache(cacheKey, function (err, u8) {
|
||||
if (err || !u8) { return void fetch(); }
|
||||
CB(void 0, u8);
|
||||
});
|
||||
|
||||
return {
|
||||
cancel: function () {
|
||||
if (xhr && xhr.abort) { xhr.abort(); }
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
Util.dataURIToBlob = function (dataURI) {
|
||||
|
|
|
@ -6,10 +6,11 @@ define([
|
|||
'/common/common-hash.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/outer/network-config.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/common/pinpad.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/chainpad/chainpad.dist.js',
|
||||
], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Pinpad, nThen) {
|
||||
], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Cache, Pinpad, nThen) {
|
||||
var finish = function (S, err, doc) {
|
||||
if (S.done) { return; }
|
||||
S.cb(err, doc);
|
||||
|
@ -92,7 +93,8 @@ define([
|
|||
validateKey: secret.keys.validateKey || undefined,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
logLevel: 0,
|
||||
initialState: opt.initialState
|
||||
initialState: opt.initialState,
|
||||
Cache: Cache
|
||||
};
|
||||
return config;
|
||||
};
|
||||
|
@ -132,9 +134,11 @@ define([
|
|||
};
|
||||
|
||||
config.onError = function (info) {
|
||||
console.warn(info);
|
||||
finish(Session, info.error);
|
||||
};
|
||||
config.onChannelError = function (info) {
|
||||
console.error(info);
|
||||
finish(Session, info.error);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ define([
|
|||
'/customize/messages.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/common/common-messaging.js',
|
||||
'/common/common-constants.js',
|
||||
'/common/common-feedback.js',
|
||||
|
@ -14,7 +15,7 @@ define([
|
|||
|
||||
'/customize/application_config.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
], function (Config, Messages, Util, Hash,
|
||||
], function (Config, Messages, Util, Hash, Cache,
|
||||
Messaging, Constants, Feedback, Visible, UserObject, LocalStore, Channel, Block,
|
||||
AppConfig, Nthen) {
|
||||
|
||||
|
@ -701,7 +702,7 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
common.useFile = function (Crypt, cb, optsPut) {
|
||||
common.useFile = function (Crypt, cb, optsPut, onProgress) {
|
||||
var fileHost = Config.fileHost || window.location.origin;
|
||||
var data = common.fromFileData;
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
|
@ -758,7 +759,9 @@ define([
|
|||
return void cb(err);
|
||||
}
|
||||
u8 = _u8;
|
||||
}));
|
||||
}), function (progress) {
|
||||
onProgress(progress * 50);
|
||||
}, Cache);
|
||||
}).nThen(function (waitFor) {
|
||||
require(["/file/file-crypto.js"], waitFor(function (FileCrypto) {
|
||||
FileCrypto.decrypt(u8, key, waitFor(function (err, _res) {
|
||||
|
@ -767,7 +770,9 @@ define([
|
|||
return void cb(err);
|
||||
}
|
||||
res = _res;
|
||||
}));
|
||||
}), function (progress) {
|
||||
onProgress(50 + progress * 50);
|
||||
});
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var ext = Util.parseFilename(data.title).ext;
|
||||
|
@ -991,6 +996,8 @@ define([
|
|||
pad.onJoinEvent = Util.mkEvent();
|
||||
pad.onLeaveEvent = Util.mkEvent();
|
||||
pad.onDisconnectEvent = Util.mkEvent();
|
||||
pad.onCacheEvent = Util.mkEvent();
|
||||
pad.onCacheReadyEvent = Util.mkEvent();
|
||||
pad.onConnectEvent = Util.mkEvent();
|
||||
pad.onErrorEvent = Util.mkEvent();
|
||||
pad.onMetadataEvent = Util.mkEvent();
|
||||
|
@ -1003,6 +1010,10 @@ define([
|
|||
postMessage("GIVE_PAD_ACCESS", data, cb);
|
||||
};
|
||||
|
||||
common.onCorruptedCache = function (channel) {
|
||||
postMessage("CORRUPTED_CACHE", channel);
|
||||
};
|
||||
|
||||
common.setPadMetadata = function (data, cb) {
|
||||
postMessage('SET_PAD_METADATA', data, cb);
|
||||
};
|
||||
|
@ -1956,6 +1967,8 @@ define([
|
|||
PAD_JOIN: common.padRpc.onJoinEvent.fire,
|
||||
PAD_LEAVE: common.padRpc.onLeaveEvent.fire,
|
||||
PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire,
|
||||
PAD_CACHE: common.padRpc.onCacheEvent.fire,
|
||||
PAD_CACHE_READY: common.padRpc.onCacheReadyEvent.fire,
|
||||
PAD_CONNECT: common.padRpc.onConnectEvent.fire,
|
||||
PAD_ERROR: common.padRpc.onErrorEvent.fire,
|
||||
PAD_METADATA: common.padRpc.onMetadataEvent.fire,
|
||||
|
|
|
@ -303,14 +303,22 @@ define([
|
|||
return renderParagraph(p);
|
||||
};
|
||||
|
||||
// Note: iframe, video and audio are used in mediatags and are allowed in rich text pads.
|
||||
var forbiddenTags = [
|
||||
'SCRIPT',
|
||||
'IFRAME',
|
||||
//'IFRAME',
|
||||
'OBJECT',
|
||||
'APPLET',
|
||||
'VIDEO', // privacy implications of videos are the same as images
|
||||
'AUDIO', // same with audio
|
||||
//'VIDEO', // privacy implications of videos are the same as images
|
||||
//'AUDIO', // same with audio
|
||||
'SOURCE'
|
||||
];
|
||||
var restrictedTags = [
|
||||
'IFRAME',
|
||||
'VIDEO',
|
||||
'AUDIO'
|
||||
];
|
||||
|
||||
var unsafeTag = function (info) {
|
||||
/*if (info.node && $(info.node).parents('media-tag').length) {
|
||||
// Do not remove elements inside a media-tag
|
||||
|
@ -347,9 +355,16 @@ define([
|
|||
parent.removeChild(node);
|
||||
};
|
||||
|
||||
// Only allow iframe, video and audio with local source
|
||||
var checkSrc = function (root) {
|
||||
if (restrictedTags.indexOf(root.nodeName.toUpperCase()) === -1) { return true; }
|
||||
return root.getAttribute && /^blob\:/.test(root.getAttribute('src'));
|
||||
};
|
||||
|
||||
var removeForbiddenTags = function (root) {
|
||||
if (!root) { return; }
|
||||
if (forbiddenTags.indexOf(root.nodeName.toUpperCase()) !== -1) { removeNode(root); }
|
||||
if (!checkSrc(root)) { removeNode(root); }
|
||||
slice(root.children).forEach(removeForbiddenTags);
|
||||
};
|
||||
|
||||
|
@ -658,7 +673,7 @@ define([
|
|||
$(contextMenu.menu).find('li').show();
|
||||
contextMenu.show(e);
|
||||
});
|
||||
if ($mt.children().length) {
|
||||
if ($mt.children().length && $mt[0]._mediaObject) {
|
||||
$mt.off('click dblclick preview');
|
||||
$mt.on('preview', onPreview($mt));
|
||||
if ($mt.find('img').length) {
|
||||
|
@ -668,15 +683,15 @@ define([
|
|||
}
|
||||
return;
|
||||
}
|
||||
MediaTag(el);
|
||||
var mediaObject = MediaTag(el);
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.type === 'childList') {
|
||||
var list_values = slice(mutation.target.children)
|
||||
var list_values = slice(el.children)
|
||||
.map(function (el) { return el.outerHTML; })
|
||||
.join('');
|
||||
mediaMap[mutation.target.getAttribute('src')] = list_values;
|
||||
observer.disconnect();
|
||||
mediaMap[el.getAttribute('src')] = list_values;
|
||||
if (mediaObject.complete) { observer.disconnect(); }
|
||||
}
|
||||
});
|
||||
$mt.off('click dblclick preview');
|
||||
|
@ -689,6 +704,7 @@ define([
|
|||
});
|
||||
observer.observe(el, {
|
||||
attributes: false,
|
||||
subtree: true,
|
||||
childList: true,
|
||||
characterData: false
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ define([
|
|||
|
||||
var APP = window.APP = {
|
||||
editable: false,
|
||||
online: true,
|
||||
mobile: function () {
|
||||
if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; }
|
||||
else { return $('body').width() <= 600; }
|
||||
|
@ -267,13 +268,25 @@ define([
|
|||
};
|
||||
|
||||
// Handle disconnect/reconnect
|
||||
var setEditable = function (state, isHistory) {
|
||||
// If isHistory and isSf are both false, update the "APP.online" flag
|
||||
// If isHistory is true, update the "APP.history" flag
|
||||
// isSf is used to detect offline shared folders: setEditable is called on displayDirectory
|
||||
var setEditable = function (state, isHistory, isSf) {
|
||||
if (APP.closed || !APP.$content || !$.contains(document.documentElement, APP.$content[0])) { return; }
|
||||
if (isHistory) {
|
||||
APP.history = !state;
|
||||
} else if (!isSf) {
|
||||
APP.online = state;
|
||||
}
|
||||
state = APP.online && !APP.history && state;
|
||||
APP.editable = !APP.readOnly && state;
|
||||
|
||||
if (!state) {
|
||||
APP.$content.addClass('cp-app-drive-readonly');
|
||||
if (!isHistory) {
|
||||
if (!APP.history || !APP.online) {
|
||||
$('#cp-app-drive-connection-state').show();
|
||||
} else {
|
||||
$('#cp-app-drive-connection-state').hide();
|
||||
}
|
||||
$('[draggable="true"]').attr('draggable', false);
|
||||
}
|
||||
|
@ -3670,6 +3683,15 @@ define([
|
|||
}
|
||||
|
||||
var readOnlyFolder = false;
|
||||
|
||||
// If the shared folder is offline, add the "DISCONNECTED" banner, otherwise
|
||||
// use the normal "editable" behavior (based on drive offline or history mode)
|
||||
if (sfId && manager.folders[sfId].offline) {
|
||||
setEditable(false, false, true);
|
||||
} else {
|
||||
setEditable(true, false, true);
|
||||
}
|
||||
|
||||
if (APP.readOnly) {
|
||||
// Read-only drive (team?)
|
||||
$content.prepend($readOnly.clone());
|
||||
|
@ -4149,6 +4171,17 @@ define([
|
|||
data.name = Util.fixFileName(folderName);
|
||||
data.folderName = Util.fixFileName(folderName) + '.zip';
|
||||
|
||||
var uo = manager.user.userObject;
|
||||
if (sfId && manager.folders[sfId]) {
|
||||
uo = manager.folders[sfId].userObject;
|
||||
}
|
||||
if (uo.getFilesRecursively) {
|
||||
data.list = uo.getFilesRecursively(folderElement).map(function (el) {
|
||||
var d = uo.getFileData(el);
|
||||
return d.channel;
|
||||
});
|
||||
}
|
||||
|
||||
APP.FM.downloadFolder(data, function (err, obj) {
|
||||
console.log(err, obj);
|
||||
console.log('DONE');
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
define([
|
||||
], function () {
|
||||
var S = {};
|
||||
|
||||
S.create = function (sframeChan) {
|
||||
var getBlobCache = function (id, cb) {
|
||||
sframeChan.query('Q_GET_BLOB_CACHE', {id:id}, function (err, data) {
|
||||
var e = err || (data && data.error);
|
||||
if (e) { return void cb(e); }
|
||||
if (!data || typeof(data) !== "object") { return void cb('EINVAL'); }
|
||||
cb(null, data);
|
||||
}, { raw: true });
|
||||
};
|
||||
var setBlobCache = function (id, u8, cb) {
|
||||
sframeChan.query('Q_SET_BLOB_CACHE', {
|
||||
id: id,
|
||||
u8: u8
|
||||
}, function (err, data) {
|
||||
var e = err || (data && data.error) || undefined;
|
||||
cb(e);
|
||||
}, { raw: true });
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
getBlobCache: getBlobCache,
|
||||
setBlobCache: setBlobCache
|
||||
};
|
||||
};
|
||||
|
||||
return S;
|
||||
});
|
||||
|
|
@ -17,11 +17,17 @@ define([
|
|||
var Nacl = window.nacl;
|
||||
|
||||
// Configure MediaTags to use our local viewer
|
||||
// This file is loaded by sframe-common so the following config is used in all the inner apps
|
||||
if (MediaTag) {
|
||||
MediaTag.setDefaultConfig('pdf', {
|
||||
viewer: '/common/pdfjs/web/viewer.html'
|
||||
});
|
||||
MediaTag.setDefaultConfig('download', {
|
||||
text: Messages.mediatag_saveButton,
|
||||
textDl: Messages.mediatag_loadButton,
|
||||
});
|
||||
}
|
||||
MT.MediaTag = MediaTag;
|
||||
|
||||
// Cache of the avatars outer html (including <media-tag>)
|
||||
var avatars = {};
|
||||
|
@ -68,7 +74,7 @@ define([
|
|||
childList: true,
|
||||
characterData: false
|
||||
});
|
||||
MediaTag($tag[0]).on('error', function (data) {
|
||||
MediaTag($tag[0], {force: true}).on('error', function (data) {
|
||||
console.error(data);
|
||||
});
|
||||
};
|
||||
|
@ -241,7 +247,6 @@ define([
|
|||
var locked = false;
|
||||
var show = function (_i) {
|
||||
if (locked) { return; }
|
||||
locked = true;
|
||||
if (_i < 0) { i = 0; }
|
||||
else if (_i > tags.length -1) { i = tags.length - 1; }
|
||||
else { i = _i; }
|
||||
|
@ -285,7 +290,6 @@ define([
|
|||
if (_key) { key = 'cryptpad:' + Nacl.util.encodeBase64(_key); }
|
||||
}
|
||||
if (!src || !key) {
|
||||
locked = false;
|
||||
$spinner.hide();
|
||||
return void UI.log(Messages.error);
|
||||
}
|
||||
|
@ -299,13 +303,18 @@ define([
|
|||
locked = false;
|
||||
$spinner.hide();
|
||||
UI.log(Messages.error);
|
||||
}).on('progress', function () {
|
||||
$spinner.hide();
|
||||
locked = true;
|
||||
}).on('complete', function () {
|
||||
locked = false;
|
||||
$spinner.hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function() {
|
||||
locked = false;
|
||||
$spinner.hide();
|
||||
});
|
||||
});
|
||||
|
@ -377,6 +386,14 @@ define([
|
|||
'tabindex': '-1',
|
||||
'data-icon': "fa-eye",
|
||||
}, Messages.pad_mediatagPreview)),
|
||||
h('li.cp-svg', h('a.cp-app-code-context-openin.dropdown-item', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': "fa-external-link",
|
||||
}, Messages.pad_mediatagOpen)),
|
||||
h('li.cp-svg', h('a.cp-app-code-context-share.dropdown-item', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': "fa-shhare-alt",
|
||||
}, Messages.pad_mediatagShare)),
|
||||
h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': "fa-cloud-upload",
|
||||
|
@ -413,12 +430,29 @@ define([
|
|||
}
|
||||
else if ($this.hasClass("cp-app-code-context-download")) {
|
||||
var media = Util.find($mt, [0, '_mediaObject']);
|
||||
if (!media) { return void console.error('no media'); }
|
||||
if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); }
|
||||
if (!(media && media._blob)) { return void console.error($mt); }
|
||||
window.saveAs(media._blob.content, media.name);
|
||||
}
|
||||
else if ($this.hasClass("cp-app-code-context-open")) {
|
||||
$mt.trigger('preview');
|
||||
}
|
||||
else if ($this.hasClass("cp-app-code-context-openin")) {
|
||||
var hash = common.getHashFromMediaTag($mt);
|
||||
common.openURL(Hash.hashToHref(hash, 'file'));
|
||||
}
|
||||
else if ($this.hasClass("cp-app-code-context-share")) {
|
||||
var data = {
|
||||
file: true,
|
||||
pathname: '/file/',
|
||||
hashes: {
|
||||
fileHash: common.getHashFromMediaTag($mt)
|
||||
},
|
||||
title: Util.find($mt[0], ['_mediaObject', 'name']) || ''
|
||||
};
|
||||
common.getSframeChannel().event('EV_SHARE_OPEN', data);
|
||||
}
|
||||
});
|
||||
|
||||
return m;
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
define([
|
||||
'jquery',
|
||||
'/common/cryptget.js',
|
||||
'/file/file-crypto.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/hyperscript.js',
|
||||
'/common/common-feedback.js',
|
||||
'/common/inner/cache.js',
|
||||
'/customize/messages.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/saferphore/index.js',
|
||||
'/bower_components/jszip/dist/jszip.min.js',
|
||||
], function ($, Crypt, FileCrypto, Hash, Util, UI, h, Feedback, Messages, nThen, Saferphore, JsZip) {
|
||||
], function ($, FileCrypto, Hash, Util, UI, h, Feedback,
|
||||
Cache, Messages, nThen, Saferphore, JsZip) {
|
||||
var saveAs = window.saveAs;
|
||||
|
||||
var sanitize = function (str) {
|
||||
|
@ -53,9 +54,6 @@ define([
|
|||
|
||||
var _downloadFile = function (ctx, fData, cb, updateProgress) {
|
||||
var cancelled = false;
|
||||
var cancel = function () {
|
||||
cancelled = true;
|
||||
};
|
||||
var href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var hash = parsed.hash;
|
||||
|
@ -63,10 +61,13 @@ define([
|
|||
var secret = Hash.getSecrets('file', hash, fData.password);
|
||||
var src = (ctx.fileHost || '') + Hash.getBlobPathFromHex(secret.channel);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
Util.fetch(src, function (err, u8) {
|
||||
|
||||
var fetchObj, decryptObj;
|
||||
|
||||
fetchObj = Util.fetch(src, function (err, u8) {
|
||||
if (cancelled) { return; }
|
||||
if (err) { return void cb('E404'); }
|
||||
FileCrypto.decrypt(u8, key, function (err, res) {
|
||||
decryptObj = FileCrypto.decrypt(u8, key, function (err, res) {
|
||||
if (cancelled) { return; }
|
||||
if (err) { return void cb(err); }
|
||||
if (!res.content) { return void cb('EEMPTY'); }
|
||||
|
@ -78,8 +79,25 @@ define([
|
|||
content: res.content,
|
||||
download: dl
|
||||
});
|
||||
}, updateProgress && updateProgress.progress2);
|
||||
}, updateProgress && updateProgress.progress);
|
||||
}, function (data) {
|
||||
if (cancelled) { return; }
|
||||
if (updateProgress && updateProgress.progress2) {
|
||||
updateProgress.progress2(data);
|
||||
}
|
||||
});
|
||||
}, function (data) {
|
||||
if (cancelled) { return; }
|
||||
if (updateProgress && updateProgress.progress) {
|
||||
updateProgress.progress(data);
|
||||
}
|
||||
}, ctx.cache);
|
||||
|
||||
var cancel = function () {
|
||||
cancelled = true;
|
||||
if (fetchObj && fetchObj.cancel) { fetchObj.cancel(); }
|
||||
if (decryptObj && decryptObj.cancel) { decryptObj.cancel(); }
|
||||
};
|
||||
|
||||
return {
|
||||
cancel: cancel
|
||||
};
|
||||
|
@ -162,10 +180,10 @@ define([
|
|||
if (ctx.stop) { return; }
|
||||
if (to) { clearTimeout(to); }
|
||||
//setTimeout(g, 2000);
|
||||
g();
|
||||
w();
|
||||
ctx.done++;
|
||||
ctx.updateProgress('download', {max: ctx.max, current: ctx.done});
|
||||
g();
|
||||
w();
|
||||
};
|
||||
|
||||
var error = function (err) {
|
||||
|
@ -274,7 +292,7 @@ define([
|
|||
};
|
||||
|
||||
// Main function. Create the empty zip and fill it starting from drive.root
|
||||
var create = function (data, getPad, fileHost, cb, progress) {
|
||||
var create = function (data, getPad, fileHost, cb, progress, cache) {
|
||||
if (!data || !data.uo || !data.uo.drive) { return void cb('EEMPTY'); }
|
||||
var sem = Saferphore.create(5);
|
||||
var ctx = {
|
||||
|
@ -288,7 +306,8 @@ define([
|
|||
sem: sem,
|
||||
updateProgress: progress,
|
||||
max: 0,
|
||||
done: 0
|
||||
done: 0,
|
||||
cache: cache
|
||||
};
|
||||
var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData;
|
||||
progress('reading', -1);
|
||||
|
@ -312,13 +331,14 @@ define([
|
|||
delete ctx.zip;
|
||||
};
|
||||
return {
|
||||
stop: stop
|
||||
stop: stop,
|
||||
cancel: stop
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var _downloadFolder = function (ctx, data, cb, updateProgress) {
|
||||
create(data, ctx.get, ctx.fileHost, function (blob, errors) {
|
||||
return create(data, ctx.get, ctx.fileHost, function (blob, errors) {
|
||||
if (errors && errors.length) { console.error(errors); } // TODO show user errors
|
||||
var dl = function () {
|
||||
saveAs(blob, data.folderName);
|
||||
|
@ -332,10 +352,13 @@ define([
|
|||
if (typeof progress.current !== "number") { return; }
|
||||
updateProgress.folderProgress(progress.current / progress.max);
|
||||
}
|
||||
else if (state === "done") {
|
||||
updateProgress.folderProgress(1);
|
||||
else if (state === "compressing") {
|
||||
updateProgress.folderProgress(2);
|
||||
}
|
||||
});
|
||||
else if (state === "done") {
|
||||
updateProgress.folderProgress(3);
|
||||
}
|
||||
}, ctx.cache);
|
||||
};
|
||||
|
||||
var createExportUI = function (origin) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
(function(name, definition) {
|
||||
if (typeof module !== 'undefined') { module.exports = definition(); }
|
||||
else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); }
|
||||
else { this[name] = definition(); }
|
||||
}('MediaTag', function() {
|
||||
(function (window) {
|
||||
var factory = function () {
|
||||
var Promise = window.Promise;
|
||||
var cache;
|
||||
var cypherChunkLength = 131088;
|
||||
|
||||
|
@ -63,7 +61,8 @@
|
|||
],
|
||||
pdf: {},
|
||||
download: {
|
||||
text: "Download"
|
||||
text: "Save",
|
||||
textDl: "Load attachment"
|
||||
},
|
||||
Plugins: {
|
||||
/**
|
||||
|
@ -114,8 +113,8 @@
|
|||
},
|
||||
download: function (metadata, url, content, cfg, cb) {
|
||||
var btn = document.createElement('button');
|
||||
btn.setAttribute('class', 'btn btn-success');
|
||||
btn.innerHTML = cfg.download.text + '<br>' +
|
||||
btn.setAttribute('class', 'btn btn-default');
|
||||
btn.innerHTML = '<i class="fa fa-save"></i>' + cfg.download.text + '<br>' +
|
||||
(metadata.name ? '<b>' + fixHTML(metadata.name) + '</b>' : '');
|
||||
btn.addEventListener('click', function () {
|
||||
saveFile(content, url, metadata.name);
|
||||
|
@ -125,28 +124,185 @@
|
|||
}
|
||||
};
|
||||
|
||||
var makeProgressBar = function (cfg, mediaObject) {
|
||||
if (mediaObject.bar) { return; }
|
||||
mediaObject.bar = true;
|
||||
var style = (function(){/*
|
||||
.mediatag-progress-container {
|
||||
position: relative;
|
||||
border: 1px solid #0087FF;
|
||||
background: white;
|
||||
height: 25px;
|
||||
display: inline-flex;
|
||||
width: 200px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
}
|
||||
.mediatag-progress-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #0087FF;
|
||||
width: 0%;
|
||||
}
|
||||
.mediatag-progress-text {
|
||||
height: 25px;
|
||||
width: 50px;
|
||||
margin-left: 5px;
|
||||
line-height: 25px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
color: #3F4141;
|
||||
font-weight: bold;
|
||||
}
|
||||
*/}).toString().slice(14, -3);
|
||||
var container = document.createElement('div');
|
||||
container.classList.add('mediatag-progress-container');
|
||||
var bar = document.createElement('div');
|
||||
bar.classList.add('mediatag-progress-bar');
|
||||
container.appendChild(bar);
|
||||
|
||||
// Download a blob from href
|
||||
var download = function (src, _cb) {
|
||||
var text = document.createElement('span');
|
||||
text.classList.add('mediatag-progress-text');
|
||||
text.innerText = '0%';
|
||||
|
||||
mediaObject.on('progress', function (obj) {
|
||||
var percent = obj.progress;
|
||||
text.innerText = (Math.round(percent*10))/10+'%';
|
||||
bar.setAttribute('style', 'width:'+percent+'%;');
|
||||
});
|
||||
|
||||
mediaObject.tag.innerHTML = '<style>'+style+'</style>';
|
||||
mediaObject.tag.appendChild(container);
|
||||
mediaObject.tag.appendChild(text);
|
||||
};
|
||||
var makeDownloadButton = function (cfg, mediaObject, size, cb) {
|
||||
var metadata = cfg.metadata || {};
|
||||
var i = '<i class="fa fa-paperclip"></i>';
|
||||
var name = metadata.name ? '<span class="mediatag-download-name">'+ i +'<b>'+
|
||||
fixHTML(metadata.name)+'</b></span>' : '';
|
||||
var btn = document.createElement('button');
|
||||
btn.setAttribute('class', 'btn btn-default mediatag-download-btn');
|
||||
btn.innerHTML = name + '<span>' + (name ? '' : i) +
|
||||
cfg.download.textDl + ' <b>(' + size + 'MB)</b></span>';
|
||||
btn.addEventListener('click', function () {
|
||||
makeProgressBar(cfg, mediaObject);
|
||||
var a = (cfg.body || document).querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button.mediatag-download-btn');
|
||||
for(var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== btn) { a[i].click(); }
|
||||
}
|
||||
cb();
|
||||
});
|
||||
mediaObject.tag.innerHTML = '';
|
||||
mediaObject.tag.appendChild(btn);
|
||||
};
|
||||
|
||||
var getCacheKey = function (src) {
|
||||
var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes
|
||||
var idx = _src.lastIndexOf('/');
|
||||
var cacheKey = _src.slice(idx+1);
|
||||
if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; }
|
||||
return cacheKey;
|
||||
};
|
||||
|
||||
var getBlobCache = function (id, cb) {
|
||||
if (!config.Cache || typeof(config.Cache.getBlobCache) !== "function") {
|
||||
return void cb('EINVAL');
|
||||
}
|
||||
config.Cache.getBlobCache(id, cb);
|
||||
};
|
||||
var setBlobCache = function (id, u8, cb) {
|
||||
if (!config.Cache || typeof(config.Cache.setBlobCache) !== "function") {
|
||||
return void cb('EINVAL');
|
||||
}
|
||||
config.Cache.setBlobCache(id, u8, cb);
|
||||
};
|
||||
|
||||
var getFileSize = function (src, _cb) {
|
||||
var cb = function (e, res) {
|
||||
_cb(e, res);
|
||||
cb = function () {};
|
||||
};
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', src, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
var cacheKey = getCacheKey(src);
|
||||
|
||||
xhr.onerror = function () { return void cb("XHR_ERROR"); };
|
||||
xhr.onload = function () {
|
||||
// Error?
|
||||
if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }
|
||||
|
||||
var arrayBuffer = xhr.response;
|
||||
if (arrayBuffer) { cb(null, new Uint8Array(arrayBuffer)); }
|
||||
var check = function () {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("HEAD", src);
|
||||
xhr.onerror = function () { return void cb("XHR_ERROR"); };
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState === this.DONE) {
|
||||
cb(null, Number(xhr.getResponseHeader("Content-Length")));
|
||||
}
|
||||
};
|
||||
xhr.onload = function () {
|
||||
if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
xhr.send(null);
|
||||
if (!cacheKey) { return void check(); }
|
||||
|
||||
getBlobCache(cacheKey, function (err, u8) {
|
||||
if (err || !u8) { return void check(); }
|
||||
cb(null, 0);
|
||||
});
|
||||
};
|
||||
|
||||
// Download a blob from href
|
||||
var download = function (src, _cb, progressCb) {
|
||||
var cb = function (e, res) {
|
||||
_cb(e, res);
|
||||
cb = function () {};
|
||||
};
|
||||
|
||||
var cacheKey = getCacheKey(src);
|
||||
|
||||
var fetch = function () {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', src, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
var progress = function (offset) {
|
||||
progressCb(offset * 100);
|
||||
};
|
||||
xhr.addEventListener("progress", function (evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = evt.loaded / evt.total;
|
||||
progress(percentComplete);
|
||||
}
|
||||
}, false);
|
||||
|
||||
xhr.onerror = function () { return void cb("XHR_ERROR"); };
|
||||
xhr.onload = function () {
|
||||
// Error?
|
||||
if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }
|
||||
|
||||
var arrayBuffer = xhr.response;
|
||||
if (arrayBuffer) {
|
||||
var u8 = new Uint8Array(arrayBuffer);
|
||||
if (cacheKey) {
|
||||
return void setBlobCache(cacheKey, u8, function () {
|
||||
cb(null, u8);
|
||||
});
|
||||
}
|
||||
cb(null, u8);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
if (!cacheKey) { return void fetch(); }
|
||||
|
||||
getBlobCache(cacheKey, function (err, u8) {
|
||||
if (err || !u8) { return void fetch(); }
|
||||
cb(null, u8);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Decryption tools
|
||||
|
@ -192,6 +348,95 @@
|
|||
}
|
||||
};
|
||||
|
||||
// The metadata size can go up to 65535 (16 bits - 2 bytes)
|
||||
// The first 8 bits are stored in A[0]
|
||||
// The last 8 bits are stored in A[0]
|
||||
var uint8ArrayJoin = function (AA) {
|
||||
var l = 0;
|
||||
var i = 0;
|
||||
for (; i < AA.length; i++) { l += AA[i].length; }
|
||||
var C = new Uint8Array(l);
|
||||
|
||||
i = 0;
|
||||
for (var offset = 0; i < AA.length; i++) {
|
||||
C.set(AA[i], offset);
|
||||
offset += AA[i].length;
|
||||
}
|
||||
return C;
|
||||
};
|
||||
var fetchMetadata = function (src, _cb) {
|
||||
var cb = function (e, res) {
|
||||
_cb(e, res);
|
||||
cb = function () {};
|
||||
};
|
||||
|
||||
var cacheKey = getCacheKey(src);
|
||||
|
||||
var fetch = function () {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', src, true);
|
||||
xhr.setRequestHeader('Range', 'bytes=0-1');
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
xhr.onerror = function () { return void cb("XHR_ERROR"); };
|
||||
xhr.onload = function () {
|
||||
// Error?
|
||||
if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }
|
||||
var res = new Uint8Array(xhr.response);
|
||||
var size = Decrypt.decodePrefix(res);
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
|
||||
xhr2.open("GET", src, true);
|
||||
xhr2.setRequestHeader('Range', 'bytes=2-' + (size + 2));
|
||||
xhr2.responseType = 'arraybuffer';
|
||||
xhr2.onload = function () {
|
||||
if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }
|
||||
var res2 = new Uint8Array(xhr2.response);
|
||||
var all = uint8ArrayJoin([res, res2]);
|
||||
cb(void 0, all);
|
||||
};
|
||||
xhr2.send(null);
|
||||
};
|
||||
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
if (!cacheKey) { return void fetch(); }
|
||||
|
||||
getBlobCache(cacheKey, function (err, u8) {
|
||||
if (err || !u8) { return void fetch(); }
|
||||
|
||||
var size = Decrypt.decodePrefix(u8.subarray(0,2));
|
||||
console.error(size);
|
||||
|
||||
cb(null, u8.subarray(0, size+2));
|
||||
});
|
||||
};
|
||||
var decryptMetadata = function (u8, key) {
|
||||
var prefix = u8.subarray(0, 2);
|
||||
var metadataLength = Decrypt.decodePrefix(prefix);
|
||||
|
||||
var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
|
||||
var metaChunk = window.nacl.secretbox.open(metaBox, Decrypt.createNonce(), key);
|
||||
|
||||
try {
|
||||
return JSON.parse(window.nacl.util.encodeUTF8(metaChunk));
|
||||
}
|
||||
catch (e) { return null; }
|
||||
};
|
||||
var fetchDecryptedMetadata = function (src, strKey, cb) {
|
||||
if (typeof(src) !== 'string') {
|
||||
return window.setTimeout(function () {
|
||||
cb('NO_SOURCE');
|
||||
});
|
||||
}
|
||||
fetchMetadata(src, function (e, buffer) {
|
||||
if (e) { return cb(e); }
|
||||
var key = Decrypt.getKeyFromStr(strKey);
|
||||
cb(void 0, decryptMetadata(buffer, key));
|
||||
});
|
||||
};
|
||||
|
||||
// Decrypts a Uint8Array with the given key.
|
||||
var decrypt = function (u8, strKey, done, progressCb) {
|
||||
var Nacl = window.nacl;
|
||||
|
@ -372,6 +617,7 @@
|
|||
var handlers = cfg.handlers || {
|
||||
'progress': [],
|
||||
'complete': [],
|
||||
'metadata': [],
|
||||
'error': []
|
||||
};
|
||||
|
||||
|
@ -422,6 +668,7 @@
|
|||
|
||||
// End media-tag rendering: display the tag and emit the event
|
||||
var end = function (decrypted) {
|
||||
mediaObject.complete = true;
|
||||
process(mediaObject, decrypted, cfg, function (err) {
|
||||
if (err) { return void emit('error', err); }
|
||||
mediaObject._blob = decrypted;
|
||||
|
@ -429,32 +676,78 @@
|
|||
});
|
||||
};
|
||||
|
||||
// If we have the blob in our cache, don't download & decrypt it again, just display
|
||||
if (cache[uid]) {
|
||||
end(cache[uid]);
|
||||
return mediaObject;
|
||||
}
|
||||
var error = function (err) {
|
||||
mediaObject.tag.innerHTML = '<img style="width: 100px; height: 100px;" src="/images/broken.png">';
|
||||
emit('error', err);
|
||||
};
|
||||
|
||||
// Download the encrypted blob
|
||||
download(src, function (err, u8Encrypted) {
|
||||
if (err) {
|
||||
if (err === "XHR_ERROR 404") {
|
||||
mediaObject.tag.innerHTML = '<img style="width: 100px; height: 100px;" src="/images/broken.png">';
|
||||
}
|
||||
return void emit('error', err);
|
||||
}
|
||||
// Decrypt the blob
|
||||
decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) {
|
||||
if (errDecryption) {
|
||||
return void emit('error', errDecryption);
|
||||
}
|
||||
// Cache and display the decrypted blob
|
||||
cache[uid] = u8Decrypted;
|
||||
end(u8Decrypted);
|
||||
}, function (progress) {
|
||||
emit('progress', {
|
||||
progress: progress
|
||||
var getCache = function () {
|
||||
var c = cache[uid];
|
||||
if (!c || !c.promise || !c.mt) { return; }
|
||||
return c;
|
||||
};
|
||||
|
||||
var dl = function () {
|
||||
// Download the encrypted blob
|
||||
cache[uid] = getCache() || {
|
||||
promise: new Promise(function (resolve, reject) {
|
||||
download(src, function (err, u8Encrypted) {
|
||||
if (err) {
|
||||
return void reject(err);
|
||||
}
|
||||
// Decrypt the blob
|
||||
decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) {
|
||||
if (errDecryption) {
|
||||
return void reject(errDecryption);
|
||||
}
|
||||
emit('metadata', u8Decrypted.metadata);
|
||||
resolve(u8Decrypted);
|
||||
}, function (progress) {
|
||||
emit('progress', {
|
||||
progress: 50+0.5*progress
|
||||
});
|
||||
});
|
||||
}, function (progress) {
|
||||
emit('progress', {
|
||||
progress: 0.5*progress
|
||||
});
|
||||
});
|
||||
}),
|
||||
mt: mediaObject
|
||||
};
|
||||
if (cache[uid].mt !== mediaObject) {
|
||||
// Add progress for other instances of this tag
|
||||
cache[uid].mt.on('progress', function (obj) {
|
||||
if (!mediaObject.bar && !cfg.force) { makeProgressBar(cfg, mediaObject); }
|
||||
emit('progress', {
|
||||
progress: obj.progress
|
||||
});
|
||||
});
|
||||
}
|
||||
cache[uid].promise.then(function (u8) {
|
||||
end(u8);
|
||||
}, function (err) {
|
||||
error(err);
|
||||
});
|
||||
};
|
||||
|
||||
if (cfg.force) { dl(); return mediaObject; }
|
||||
|
||||
var maxSize = typeof(config.maxDownloadSize) === "number" ? config.maxDownloadSize
|
||||
: (5 * 1024 * 1024);
|
||||
fetchDecryptedMetadata(src, strKey, function (err, md) {
|
||||
if (err) { return void error(err); }
|
||||
cfg.metadata = md;
|
||||
emit('metadata', md);
|
||||
getFileSize(src, function (err, size) {
|
||||
// If the size is smaller than the autodownload limit, load the blob.
|
||||
// If the blob is already loaded or being loaded, don't show the button.
|
||||
if (!size || size < maxSize || getCache()) {
|
||||
makeProgressBar(cfg, mediaObject);
|
||||
return void dl();
|
||||
}
|
||||
var sizeMb = Math.round(10 * size / 1024 / 1024) / 10;
|
||||
makeDownloadButton(cfg, mediaObject, sizeMb, dl);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -468,5 +761,20 @@
|
|||
config[key] = value;
|
||||
};
|
||||
|
||||
init.fetchDecryptedMetadata = fetchDecryptedMetadata;
|
||||
|
||||
return init;
|
||||
}));
|
||||
};
|
||||
|
||||
if (typeof(module) !== 'undefined' && module.exports) {
|
||||
module.exports = factory();
|
||||
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
|
||||
define([
|
||||
'/bower_components/es6-promise/es6-promise.min.js'
|
||||
], function () {
|
||||
return factory();
|
||||
});
|
||||
} else {
|
||||
// unsupported initialization
|
||||
}
|
||||
}(typeof(window) !== 'undefined'? window : {}));
|
||||
|
|
|
@ -1423,7 +1423,7 @@ define([
|
|||
console.error(e);
|
||||
callback("");
|
||||
}
|
||||
});
|
||||
}, void 0, common.getCache());
|
||||
};
|
||||
|
||||
APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder-a", APP.ooconfig);
|
||||
|
|
|
@ -10,6 +10,7 @@ define([
|
|||
'/common/common-realtime.js',
|
||||
'/common/common-messaging.js',
|
||||
'/common/pinpad.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/common/outer/sharedfolder.js',
|
||||
'/common/outer/cursor.js',
|
||||
'/common/outer/onlyoffice.js',
|
||||
|
@ -28,7 +29,7 @@ define([
|
|||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/saferphore/index.js',
|
||||
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback,
|
||||
Realtime, Messaging, Pinpad,
|
||||
Realtime, Messaging, Pinpad, Cache,
|
||||
SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History,
|
||||
NetConfig, AppConfig,
|
||||
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
|
||||
|
@ -120,10 +121,13 @@ define([
|
|||
Store.getSharedFolder = function (clientId, data, cb) {
|
||||
var s = getStore(data.teamId);
|
||||
var id = data.id;
|
||||
var proxy;
|
||||
if (!s || !s.manager) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
if (s.manager.folders[id]) {
|
||||
proxy = Util.clone(s.manager.folders[id].proxy);
|
||||
proxy.offline = Boolean(s.manager.folders[id].offline);
|
||||
// If it is loaded, return the shared folder proxy
|
||||
return void cb(s.manager.folders[id].proxy);
|
||||
return void cb(proxy);
|
||||
} else {
|
||||
// Otherwise, check if we know this shared folder
|
||||
var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
|
||||
|
@ -1133,7 +1137,7 @@ define([
|
|||
var ownedByMe = Array.isArray(owners) && owners.indexOf(edPublic) !== -1;
|
||||
|
||||
// Add the pad if it does not exist in our drive
|
||||
if (!contains || (ownedByMe && !inMyDrive)) {
|
||||
if (!contains) { // || (ownedByMe && !inMyDrive)) {
|
||||
var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']);
|
||||
if (autoStore !== 1 && !data.forceSave && !data.path && !ownedByMe) {
|
||||
// send event to inner to display the corner popup
|
||||
|
@ -1590,13 +1594,20 @@ define([
|
|||
Store.leavePad(null, data, function () {});
|
||||
};
|
||||
var conf = {
|
||||
Cache: Cache,
|
||||
onCacheStart: function () {
|
||||
postMessage(clientId, "PAD_CACHE");
|
||||
},
|
||||
onCacheReady: function () {
|
||||
postMessage(clientId, "PAD_CACHE_READY");
|
||||
},
|
||||
onReady: function (pad) {
|
||||
var padData = pad.metadata || {};
|
||||
channel.data = padData;
|
||||
if (padData && padData.validateKey && store.messenger) {
|
||||
store.messenger.storeValidateKey(data.channel, padData.validateKey);
|
||||
}
|
||||
postMessage(clientId, "PAD_READY");
|
||||
postMessage(clientId, "PAD_READY", pad.noCache);
|
||||
},
|
||||
onMessage: function (m, user, validateKey, isCp, hash) {
|
||||
channel.lastHash = hash;
|
||||
|
@ -1727,6 +1738,14 @@ define([
|
|||
channel.sendMessage(msg, clientId, cb);
|
||||
};
|
||||
|
||||
Store.corruptedCache = function (clientId, channel) {
|
||||
var chan = channels[channel];
|
||||
if (!chan || !chan.cpNf) { return; }
|
||||
Cache.clearChannel(channel);
|
||||
if (!chan.cpNf.resetCache) { return; }
|
||||
chan.cpNf.resetCache();
|
||||
};
|
||||
|
||||
// Unpin and pin the new channel in all team when changing a pad password
|
||||
Store.changePadPasswordPin = function (clientId, data, cb) {
|
||||
var oldChannel = data.oldChannel;
|
||||
|
@ -2640,6 +2659,7 @@ define([
|
|||
readOnly: false,
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
Cache: Cache,
|
||||
userName: 'fs',
|
||||
logLevel: 1,
|
||||
ChainPad: ChainPad,
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
define([
|
||||
'/common/common-util.js',
|
||||
'/bower_components/localforage/dist/localforage.min.js',
|
||||
], function (Util, localForage) {
|
||||
var S = {};
|
||||
|
||||
var cache = localForage.createInstance({
|
||||
name: "cp_cache"
|
||||
});
|
||||
|
||||
S.getBlobCache = function (id, cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
cache.getItem(id, function (err, obj) {
|
||||
if (err || !obj || !obj.c) {
|
||||
return void cb(err || 'EINVAL');
|
||||
}
|
||||
cb(null, obj.c);
|
||||
obj.t = +new Date();
|
||||
cache.setItem(id, obj);
|
||||
});
|
||||
};
|
||||
S.setBlobCache = function (id, u8, cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
if (!u8) { return void cb('EINVAL'); }
|
||||
cache.setItem(id, {
|
||||
c: u8,
|
||||
t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set)
|
||||
}, function (err) {
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
// id: channel ID or blob ID
|
||||
// returns array of messages
|
||||
S.getChannelCache = function (id, cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
cache.getItem(id, function (err, obj) {
|
||||
if (err || !obj || !Array.isArray(obj.c)) {
|
||||
return void cb(err || 'EINVAL');
|
||||
}
|
||||
cb(null, obj);
|
||||
obj.t = +new Date();
|
||||
cache.setItem(id, obj);
|
||||
});
|
||||
};
|
||||
|
||||
// Keep the last two checkpoint + any checkpoint that may exist in the last 100 messages
|
||||
// FIXME: duplicate system with sliceCpIndex from lib/hk-util.js
|
||||
var checkCheckpoints = function (array) {
|
||||
if (!Array.isArray(array)) { return; }
|
||||
// Keep the last 100 messages
|
||||
if (array.length > 100) {
|
||||
array.splice(0, array.length - 100);
|
||||
}
|
||||
// Remove every message before the first checkpoint
|
||||
var firstCpIdx;
|
||||
array.some(function (el, i) {
|
||||
if (!el.isCheckpoint) { return; }
|
||||
firstCpIdx = i;
|
||||
return true;
|
||||
});
|
||||
array.splice(0, firstCpIdx);
|
||||
};
|
||||
|
||||
S.storeCache = function (id, validateKey, val, cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
if (!Array.isArray(val) || !validateKey) { return void cb('EINVAL'); }
|
||||
checkCheckpoints(val);
|
||||
cache.setItem(id, {
|
||||
k: validateKey,
|
||||
c: val,
|
||||
t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set)
|
||||
}, function (err) {
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
S.clearChannel = function (id, cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
cache.removeItem(id, function () {
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
S.clear = function (cb) {
|
||||
cb = Util.once(Util.mkAsync(cb || function () {}));
|
||||
cache.clear(cb);
|
||||
};
|
||||
|
||||
self.CryptPad_clearIndexedDB = S.clear;
|
||||
|
||||
return S;
|
||||
});
|
|
@ -1,9 +1,10 @@
|
|||
define([
|
||||
'/common/common-constants.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/bower_components/localforage/dist/localforage.min.js',
|
||||
'/customize/application_config.js',
|
||||
], function (Constants, Hash, localForage, AppConfig) {
|
||||
], function (Constants, Hash, Cache, localForage, AppConfig) {
|
||||
var LocalStore = {};
|
||||
|
||||
LocalStore.setThumbnail = function (key, value, cb) {
|
||||
|
@ -119,7 +120,14 @@ define([
|
|||
return void AppConfig.customizeLogout(cb);
|
||||
}
|
||||
|
||||
if (cb) { cb(); }
|
||||
cb = cb || function () {};
|
||||
|
||||
try {
|
||||
Cache.clear(cb);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
cb();
|
||||
}
|
||||
};
|
||||
var loginHandlers = [];
|
||||
LocalStore.loginReload = function () {
|
||||
|
|
|
@ -2,12 +2,13 @@ define([
|
|||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
'/common/userObject.js',
|
||||
'/common/outer/cache-store.js',
|
||||
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||
'/bower_components/chainpad/chainpad.dist.js',
|
||||
], function (Hash, Util, UserObject,
|
||||
], function (Hash, Util, UserObject, Cache,
|
||||
nThen, Crypto, Listmap, ChainPad) {
|
||||
var SF = {};
|
||||
|
||||
|
@ -174,6 +175,7 @@ define([
|
|||
ChainPad: ChainPad,
|
||||
classic: true,
|
||||
network: network,
|
||||
Cache: Cache,
|
||||
metadata: {
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
owners: owners
|
||||
|
|
|
@ -88,6 +88,7 @@ define([
|
|||
CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin,
|
||||
GET_LAST_HASH: Store.getLastHash,
|
||||
GET_SNAPSHOT: Store.getSnapshot,
|
||||
CORRUPTED_CACHE: Store.corruptedCache,
|
||||
// Drive
|
||||
DRIVE_USEROBJECT: Store.userObjectCommand,
|
||||
// Settings,
|
||||
|
|
|
@ -12,6 +12,7 @@ define([
|
|||
'/common/common-feedback.js',
|
||||
'/common/outer/invitation.js',
|
||||
'/common/cryptget.js',
|
||||
'/common/outer/cache-store.js',
|
||||
|
||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
|
@ -21,7 +22,7 @@ define([
|
|||
'/bower_components/saferphore/index.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (Util, Hash, Constants, Realtime,
|
||||
ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt,
|
||||
ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, Cache,
|
||||
Listmap, Crypto, CpNetflux, ChainPad, nThen, Saferphore) {
|
||||
var Team = {};
|
||||
|
||||
|
@ -57,11 +58,11 @@ define([
|
|||
});
|
||||
proxy.on('disconnect', function () {
|
||||
team.offline = true;
|
||||
team.sendEvent('NETWORK_DISCONNECT');
|
||||
team.sendEvent('NETWORK_DISCONNECT', team.id);
|
||||
});
|
||||
proxy.on('reconnect', function () {
|
||||
team.offline = false;
|
||||
team.sendEvent('NETWORK_RECONNECT');
|
||||
team.sendEvent('NETWORK_RECONNECT', team.id);
|
||||
});
|
||||
}
|
||||
proxy.on('change', [], function (o, n, p) {
|
||||
|
@ -426,6 +427,7 @@ define([
|
|||
channel: secret.channel,
|
||||
crypto: crypto,
|
||||
ChainPad: ChainPad,
|
||||
Cache: Cache,
|
||||
metadata: {
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
},
|
||||
|
@ -573,6 +575,7 @@ define([
|
|||
logLevel: 1,
|
||||
classic: true,
|
||||
ChainPad: ChainPad,
|
||||
Cache: Cache,
|
||||
owners: [ctx.store.proxy.edPublic]
|
||||
};
|
||||
nThen(function (waitFor) {
|
||||
|
@ -931,7 +934,9 @@ define([
|
|||
if (!team) { return void cb ({error: 'ENOENT'}); }
|
||||
if (!team.roster) { return void cb({error: 'NO_ROSTER'}); }
|
||||
var state = team.roster.getState() || {};
|
||||
cb(state.metadata || {});
|
||||
var md = state.metadata || {};
|
||||
md.offline = team.offline;
|
||||
cb(md);
|
||||
};
|
||||
|
||||
var setTeamMetadata = function (ctx, data, cId, cb) {
|
||||
|
@ -1879,15 +1884,15 @@ define([
|
|||
var t = Util.clone(teams);
|
||||
Object.keys(t).forEach(function (id) {
|
||||
// If failure to load the team, don't send it
|
||||
if (ctx.teams[id]) { return; }
|
||||
if (ctx.teams[id]) {
|
||||
t[id].offline = ctx.teams[id].offline;
|
||||
return;
|
||||
}
|
||||
t[id].error = true;
|
||||
});
|
||||
cb(t);
|
||||
};
|
||||
team.execCommand = function (clientId, obj, cb) {
|
||||
if (ctx.store.offline) {
|
||||
return void cb({ error: 'OFFLINE' });
|
||||
}
|
||||
|
||||
var cmd = obj.cmd;
|
||||
var data = obj.data;
|
||||
|
@ -1911,30 +1916,36 @@ define([
|
|||
return void setTeamMetadata(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'OFFER_OWNERSHIP') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void offerOwnership(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'ANSWER_OWNERSHIP') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void answerOwnership(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'DESCRIBE_USER') {
|
||||
return void describeUser(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'INVITE_TO_TEAM') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void inviteToTeam(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'LEAVE_TEAM') {
|
||||
return void leaveTeam(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'JOIN_TEAM') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void joinTeam(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'REMOVE_USER') {
|
||||
return void removeUser(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'DELETE_TEAM') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void deleteTeam(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'CREATE_TEAM') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void createTeam(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'GET_EDITABLE_FOLDERS') {
|
||||
|
@ -1947,6 +1958,7 @@ define([
|
|||
return void getPreviewContent(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'ACCEPT_LINK_INVITATION') {
|
||||
if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); }
|
||||
return void acceptLinkInvitation(ctx, data, clientId, cb);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
define([
|
||||
'/file/file-crypto.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (FileCrypto, Hash, nThen) {
|
||||
], function (FileCrypto, Hash, Util, Cache, nThen) {
|
||||
var Nacl = window.nacl;
|
||||
var module = {};
|
||||
|
||||
|
@ -31,9 +33,11 @@ define([
|
|||
};
|
||||
|
||||
var actual = 0;
|
||||
var encryptedArr = [];
|
||||
var again = function (err, box) {
|
||||
if (err) { onError(err); }
|
||||
if (box) {
|
||||
encryptedArr.push(box);
|
||||
actual += box.length;
|
||||
var progressValue = (actual / estimate * 100);
|
||||
progressValue = Math.min(progressValue, 100);
|
||||
|
@ -55,9 +59,11 @@ define([
|
|||
var uri = ['', 'blob', id.slice(0,2), id].join('/');
|
||||
console.log("encrypted blob is now available as %s", uri);
|
||||
|
||||
|
||||
|
||||
cb();
|
||||
var box_u8 = Util.uint8ArrayJoin(encryptedArr);
|
||||
Cache.setBlobCache(id, box_u8, function (err) {
|
||||
if (err) { console.warn(err); }
|
||||
cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -65,11 +65,13 @@ define([
|
|||
cb(undefined, data.content, msg);
|
||||
};
|
||||
evReady.reg(function () {
|
||||
postMsg(JSON.stringify({
|
||||
var toSend = {
|
||||
txid: txid,
|
||||
content: content,
|
||||
q: q
|
||||
}));
|
||||
q: q,
|
||||
raw: opts.raw
|
||||
};
|
||||
postMsg(opts.raw ? toSend : JSON.stringify(toSend));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -84,12 +86,13 @@ define([
|
|||
// If the type is a query, your handler will be invoked with a reply function that takes
|
||||
// one argument (the content to reply with).
|
||||
chan.on = function (queryType, handler, quiet) {
|
||||
var h = function (data, msg) {
|
||||
var h = function (data, msg, raw) {
|
||||
handler(data.content, function (replyContent) {
|
||||
postMsg(JSON.stringify({
|
||||
var toSend = {
|
||||
txid: data.txid,
|
||||
content: replyContent
|
||||
}));
|
||||
};
|
||||
postMsg(raw ? toSend : JSON.stringify(toSend));
|
||||
}, msg);
|
||||
};
|
||||
(handlers[queryType] = handlers[queryType] || []).push(h);
|
||||
|
@ -150,7 +153,7 @@ define([
|
|||
onMsg.reg(function (msg) {
|
||||
if (!chanLoaded) { return; }
|
||||
if (!msg.data || msg.data === '_READY') { return; }
|
||||
var data = JSON.parse(msg.data);
|
||||
var data = typeof(msg.data) === "object" ? msg.data : JSON.parse(msg.data);
|
||||
if (typeof(data.ack) !== "undefined") {
|
||||
if (acks[data.txid]) { acks[data.txid](!data.ack); }
|
||||
} else if (typeof(data.q) === 'string') {
|
||||
|
@ -163,7 +166,7 @@ define([
|
|||
}));
|
||||
}
|
||||
handlers[data.q].forEach(function (f) {
|
||||
f(data || JSON.parse(msg.data), msg);
|
||||
f(data || JSON.parse(msg.data), msg, data && data.raw);
|
||||
data = undefined;
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -40,6 +40,14 @@ define([
|
|||
userObject: userObject,
|
||||
leave: leave
|
||||
};
|
||||
if (proxy.on) {
|
||||
proxy.on('disconnect', function () {
|
||||
Env.folders[id].offline = true;
|
||||
});
|
||||
proxy.on('reconnect', function () {
|
||||
Env.folders[id].online = true;
|
||||
});
|
||||
}
|
||||
return userObject;
|
||||
};
|
||||
|
||||
|
|
|
@ -467,7 +467,51 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
var noCache = false; // Prevent reload loops
|
||||
var onCorruptedCache = function () {
|
||||
if (noCache) {
|
||||
UI.errorLoadingScreen(Messages.unableToDisplay, false, function () {
|
||||
common.gotoURL('');
|
||||
});
|
||||
}
|
||||
noCache = true;
|
||||
var sframeChan = common.getSframeChannel();
|
||||
sframeChan.event("EV_CORRUPTED_CACHE");
|
||||
};
|
||||
var onCacheReady = function () {
|
||||
stateChange(STATE.DISCONNECTED);
|
||||
toolbar.offline(true);
|
||||
var newContentStr = cpNfInner.chainpad.getUserDoc();
|
||||
if (toolbar) {
|
||||
// Check if we have a new chainpad instance
|
||||
toolbar.resetChainpad(cpNfInner.chainpad);
|
||||
}
|
||||
|
||||
// Invalid cache
|
||||
if (newContentStr === '') { return void onCorruptedCache(); }
|
||||
|
||||
var privateDat = cpNfInner.metadataMgr.getPrivateData();
|
||||
var type = privateDat.app;
|
||||
|
||||
var newContent = JSON.parse(newContentStr);
|
||||
var metadata = extractMetadata(newContent);
|
||||
|
||||
// Make sure we're using the correct app for this cache
|
||||
if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) {
|
||||
return void onCorruptedCache();
|
||||
}
|
||||
|
||||
cpNfInner.metadataMgr.updateMetadata(metadata);
|
||||
newContent = normalize(newContent);
|
||||
if (!unsyncMode) {
|
||||
contentUpdate(newContent, function () { return function () {}; });
|
||||
}
|
||||
|
||||
UI.removeLoadingScreen(emitResize);
|
||||
};
|
||||
var onReady = function () {
|
||||
toolbar.offline(false);
|
||||
|
||||
var newContentStr = cpNfInner.chainpad.getUserDoc();
|
||||
if (state === STATE.DELETED) { return; }
|
||||
|
||||
|
@ -508,14 +552,19 @@ define([
|
|||
console.log("Either this is an empty document which has not been touched");
|
||||
console.log("Or else something is terribly wrong, reloading.");
|
||||
Feedback.send("NON_EMPTY_NEWDOC");
|
||||
setTimeout(function () { common.gotoURL(); }, 1000);
|
||||
// The cache may be wrong, empty it and reload after.
|
||||
waitFor.abort();
|
||||
onCorruptedCache();
|
||||
return;
|
||||
}
|
||||
console.log('updating title');
|
||||
title.updateTitle(title.defaultTitle);
|
||||
evOnDefaultContentNeeded.fire();
|
||||
}
|
||||
}).nThen(function () {
|
||||
// We have a valid chainpad, reenable cache fix in case with reconnect with
|
||||
// a corrupted cache
|
||||
noCache = false;
|
||||
|
||||
stateChange(STATE.READY);
|
||||
firstConnection = false;
|
||||
|
||||
|
@ -734,6 +783,7 @@ define([
|
|||
onRemote: onRemote,
|
||||
onLocal: onLocal,
|
||||
onInit: onInit,
|
||||
onCacheReady: onCacheReady,
|
||||
onReady: function () { evStart.reg(onReady); },
|
||||
onConnectionChange: onConnectionChange,
|
||||
onError: onError,
|
||||
|
|
|
@ -34,6 +34,7 @@ define([
|
|||
var onLocal = config.onLocal || function () { };
|
||||
var setMyID = config.setMyID || function () { };
|
||||
var onReady = config.onReady || function () { };
|
||||
var onCacheReady = config.onCacheReady || function () { };
|
||||
var onError = config.onError || function () { };
|
||||
var userName = config.userName;
|
||||
var initialState = config.initialState;
|
||||
|
@ -93,6 +94,9 @@ define([
|
|||
evInfiniteSpinner.fire();
|
||||
}, 2000);
|
||||
|
||||
sframeChan.on('EV_RT_CACHE_READY', function () {
|
||||
onCacheReady({realtime: chainpad});
|
||||
});
|
||||
sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) {
|
||||
isReady = false;
|
||||
chainpad.abort();
|
||||
|
|
|
@ -46,6 +46,7 @@ define([], function () {
|
|||
// shim between chainpad and netflux
|
||||
var msgIn = function (peer, msg) {
|
||||
try {
|
||||
if (/^\[/.test(msg)) { return msg; } // Already decrypted
|
||||
var isHk = peer.length !== 32;
|
||||
var key = isNewHash ? validateKey : false;
|
||||
var decryptedMsg = Crypto.decrypt(msg, key, isHk);
|
||||
|
@ -114,16 +115,25 @@ define([], function () {
|
|||
if (firstConnection) {
|
||||
firstConnection = false;
|
||||
// Add the handlers to the WebChannel
|
||||
padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); });
|
||||
padRpc.onJoinEvent.reg(function (m) { sframeChan.event('EV_RT_JOIN', m); });
|
||||
padRpc.onLeaveEvent.reg(function (m) { sframeChan.event('EV_RT_LEAVE', m); });
|
||||
}
|
||||
};
|
||||
|
||||
padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); });
|
||||
|
||||
padRpc.onDisconnectEvent.reg(function (permanent) {
|
||||
sframeChan.event('EV_RT_DISCONNECT', permanent);
|
||||
});
|
||||
|
||||
padRpc.onCacheReadyEvent.reg(function () {
|
||||
sframeChan.event('EV_RT_CACHE_READY');
|
||||
});
|
||||
|
||||
padRpc.onCacheEvent.reg(function () {
|
||||
sframeChan.event('EV_RT_CACHE');
|
||||
});
|
||||
|
||||
padRpc.onConnectEvent.reg(function (data) {
|
||||
onOpen(data);
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ define([
|
|||
};
|
||||
|
||||
var tableHeader = h('div.cp-fileupload-header', [
|
||||
h('div.cp-fileupload-header-title', h('span', Messages.fileuploadHeader || 'Uploaded files')),
|
||||
h('div.cp-fileupload-header-title', h('span', Messages.fileTableHeader)),
|
||||
h('div.cp-fileupload-header-close', h('span.fa.fa-times')),
|
||||
]);
|
||||
|
||||
|
@ -260,7 +260,8 @@ define([
|
|||
// name
|
||||
$('<td>').append($link).appendTo($tr);
|
||||
// size
|
||||
$('<td>').text(UIElements.prettySize(estimate)).appendTo($tr);
|
||||
var size = estimate ? UIElements.prettySize(estimate) : '';
|
||||
$(h('td.cp-fileupload-size')).text(size).appendTo($tr);
|
||||
// progress
|
||||
$('<td>', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr);
|
||||
// cancel
|
||||
|
@ -588,12 +589,11 @@ define([
|
|||
queue.next();
|
||||
};
|
||||
|
||||
/*
|
||||
var cancelled = function () {
|
||||
$row.find('.cp-fileupload-table-cancel').addClass('cancelled').html('').append(h('span.fa.fa-minus'));
|
||||
queue.inProgress = false;
|
||||
queue.next();
|
||||
};*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Update progress in the download panel, for downloading a file
|
||||
|
@ -627,6 +627,17 @@ define([
|
|||
*/
|
||||
var updateProgress = function (progressValue) {
|
||||
var text = Math.round(progressValue*100) + '%';
|
||||
if (Array.isArray(data.list)) {
|
||||
text = Messages._getKey('download_zip_file', [Math.round(progressValue * data.list.length), data.list.length]);
|
||||
}
|
||||
if (progressValue === 2) {
|
||||
text = Messages.download_zip;
|
||||
progressValue = 1;
|
||||
}
|
||||
if (progressValue === 3) {
|
||||
text = "100%";
|
||||
progressValue = 1;
|
||||
}
|
||||
$pv.text(text);
|
||||
$pb.css({
|
||||
width: (progressValue * 100) + '%'
|
||||
|
@ -638,8 +649,10 @@ define([
|
|||
fileHost: privateData.fileHost,
|
||||
get: common.getPad,
|
||||
sframeChan: sframeChan,
|
||||
cache: common.getCache()
|
||||
};
|
||||
downloadFunction(ctx, data, function (err, obj) {
|
||||
|
||||
var dl = downloadFunction(ctx, data, function (err, obj) {
|
||||
$link.prepend($('<span>', {'class': 'fa fa-external-link'}))
|
||||
.attr('href', '#')
|
||||
.click(function (e) {
|
||||
|
@ -655,19 +668,17 @@ define([
|
|||
folderProgress: updateProgress,
|
||||
});
|
||||
|
||||
/*
|
||||
var $cancel = $('<span>', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () {
|
||||
dl.cancel();
|
||||
$cancel.remove();
|
||||
$row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled);
|
||||
cancelled();
|
||||
});
|
||||
*/
|
||||
|
||||
$row.find('.cp-fileupload-table-cancel')
|
||||
.html('')
|
||||
.append(h('span.fa.fa-minus'));
|
||||
//.append($cancel);
|
||||
var $cancel = $row.find('.cp-fileupload-table-cancel').html('');
|
||||
if (dl && dl.cancel) {
|
||||
$('<span>', {
|
||||
'class': 'cp-fileupload-table-cancel-button fa fa-times'
|
||||
}).click(function () {
|
||||
dl.cancel();
|
||||
$cancel.remove();
|
||||
$row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled);
|
||||
cancelled();
|
||||
}).appendTo($cancel);
|
||||
}
|
||||
};
|
||||
|
||||
File.downloadFile = function (fData, cb) {
|
||||
|
|
|
@ -100,12 +100,13 @@ define([
|
|||
'/common/common-constants.js',
|
||||
'/common/common-feedback.js',
|
||||
'/common/outer/local-store.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/customize/application_config.js',
|
||||
'/common/test.js',
|
||||
'/common/userObject.js',
|
||||
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel,
|
||||
_SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime,
|
||||
_Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) {
|
||||
_Constants, _Feedback, _LocalStore, _Cache, _AppConfig, _Test, _UserObject) {
|
||||
CpNfOuter = _CpNfOuter;
|
||||
Cryptpad = _Cryptpad;
|
||||
Crypto = Utils.Crypto = _Crypto;
|
||||
|
@ -120,6 +121,7 @@ define([
|
|||
Utils.Constants = _Constants;
|
||||
Utils.Feedback = _Feedback;
|
||||
Utils.LocalStore = _LocalStore;
|
||||
Utils.Cache = _Cache;
|
||||
Utils.UserObject = _UserObject;
|
||||
Utils.currentPad = currentPad;
|
||||
AppConfig = _AppConfig;
|
||||
|
@ -678,6 +680,20 @@ define([
|
|||
});
|
||||
});
|
||||
|
||||
sframeChan.on('Q_GET_BLOB_CACHE', function (data, cb) {
|
||||
Utils.Cache.getBlobCache(data.id, function (err, obj) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(obj);
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_SET_BLOB_CACHE', function (data, cb) {
|
||||
if (!data || !data.u8 || typeof(data.u8) !== "object") { return void cb({error: 'EINVAL'}); }
|
||||
Utils.Cache.setBlobCache(data.id, data.u8, function (err) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) {
|
||||
Cryptpad.getAttribute(data.key, function (e, data) {
|
||||
cb({
|
||||
|
@ -1697,6 +1713,10 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
sframeChan.on('EV_CORRUPTED_CACHE', function () {
|
||||
Cryptpad.onCorruptedCache(secret.channel);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_CREATE_PAD', function (data, cb) {
|
||||
if (!isNewFile || rtStarted) { return; }
|
||||
// Create a new hash
|
||||
|
@ -1811,7 +1831,12 @@ define([
|
|||
}
|
||||
startRealtime();
|
||||
cb();
|
||||
}, cryptputCfg);
|
||||
}, cryptputCfg, function (progress) {
|
||||
sframeChan.event('EV_LOADING_INFO', {
|
||||
type: 'pad',
|
||||
progress: progress
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Start realtime outside the iframe and callback
|
||||
|
|
|
@ -11,6 +11,7 @@ define([
|
|||
'/common/sframe-common-codemirror.js',
|
||||
'/common/sframe-common-cursor.js',
|
||||
'/common/sframe-common-mailbox.js',
|
||||
'/common/inner/cache.js',
|
||||
'/common/inner/common-mediatag.js',
|
||||
'/common/metadata-manager.js',
|
||||
|
||||
|
@ -36,6 +37,7 @@ define([
|
|||
CodeMirror,
|
||||
Cursor,
|
||||
Mailbox,
|
||||
Cache,
|
||||
MT,
|
||||
MetadataMgr,
|
||||
AppConfig,
|
||||
|
@ -142,7 +144,7 @@ define([
|
|||
}
|
||||
return;
|
||||
};
|
||||
funcs.importMediaTag = function ($mt) {
|
||||
var getMtData = function ($mt) {
|
||||
if (!$mt || !$mt.is('media-tag')) { return; }
|
||||
var chanStr = $mt.attr('src');
|
||||
var keyStr = $mt.attr('data-crypto-key');
|
||||
|
@ -154,10 +156,27 @@ define([
|
|||
var channel = src.replace(/\/blob\/[0-9a-f]{2}\//i, '');
|
||||
// Get key
|
||||
var key = keyStr.replace(/cryptpad:/i, '');
|
||||
return {
|
||||
channel: channel,
|
||||
key: key
|
||||
};
|
||||
};
|
||||
funcs.getHashFromMediaTag = function ($mt) {
|
||||
var data = getMtData($mt);
|
||||
if (!data) { return; }
|
||||
return Hash.getFileHashFromKeys({
|
||||
version: 1,
|
||||
channel: data.channel,
|
||||
keys: { fileKeyStr: data.key }
|
||||
});
|
||||
};
|
||||
funcs.importMediaTag = function ($mt) {
|
||||
var data = getMtData($mt);
|
||||
if (!data) { return; }
|
||||
var metadata = $mt[0]._mediaObject._blob.metadata;
|
||||
ctx.sframeChan.query('Q_IMPORT_MEDIATAG', {
|
||||
channel: channel,
|
||||
key: key,
|
||||
channel: data.channel,
|
||||
key: data.key,
|
||||
name: metadata.name,
|
||||
type: metadata.type,
|
||||
owners: metadata.owners
|
||||
|
@ -588,6 +607,10 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
funcs.getCache = function () {
|
||||
return ctx.cache;
|
||||
};
|
||||
|
||||
/* funcs.storeLinkToClipboard = function (readOnly, cb) {
|
||||
ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) {
|
||||
if (cb) { cb(err); }
|
||||
|
@ -794,12 +817,24 @@ define([
|
|||
modules[type].onEvent(obj.data);
|
||||
});
|
||||
|
||||
ctx.cache = Cache.create(ctx.sframeChan);
|
||||
|
||||
ctx.metadataMgr.onReady(waitFor());
|
||||
|
||||
}).nThen(function () {
|
||||
var privateData = ctx.metadataMgr.getPrivateData();
|
||||
funcs.addShortcuts(window, Boolean(privateData.app));
|
||||
|
||||
var mt = Util.find(privateData, ['settings', 'general', 'mediatag-size']);
|
||||
if (MT.MediaTag && typeof(mt) === "number") {
|
||||
var maxMtSize = mt === -1 ? Infinity : mt * 1024 * 1024;
|
||||
MT.MediaTag.setDefaultConfig('maxDownloadSize', maxMtSize);
|
||||
}
|
||||
|
||||
if (MT.MediaTag && ctx.cache) {
|
||||
MT.MediaTag.setDefaultConfig('Cache', ctx.cache);
|
||||
}
|
||||
|
||||
try {
|
||||
var feedback = privateData.feedbackAllowed;
|
||||
Feedback.init(feedback);
|
||||
|
|
|
@ -1388,6 +1388,18 @@ MessengerUI, Messages) {
|
|||
}
|
||||
};
|
||||
|
||||
toolbar.offline = function (bool) {
|
||||
toolbar.connected = !bool; // Can't edit title
|
||||
toolbar.history = bool; // Stop "Initializing" state
|
||||
toolbar.isErrorState = bool; // Stop kickSpinner
|
||||
toolbar.title.toggleClass('cp-toolbar-unsync', bool); // "read only" next to the title
|
||||
if (bool && toolbar.spinner) {
|
||||
toolbar.spinner.text(Messages.offline);
|
||||
} else {
|
||||
kickSpinner(toolbar, config);
|
||||
}
|
||||
};
|
||||
|
||||
// On log out, remove permanently the realtime elements of the toolbar
|
||||
Common.onLogout(function () {
|
||||
failed();
|
||||
|
|
|
@ -28,6 +28,8 @@ define([
|
|||
// Loaded in load #2
|
||||
nThen(function (waitFor) {
|
||||
$(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
SFCommonO.initIframe(waitFor);
|
||||
}).nThen(function (waitFor) {
|
||||
var req = {
|
||||
cfg: requireConfig,
|
||||
|
|
|
@ -82,6 +82,8 @@ define([
|
|||
var readOnly = !secret.keys.editKeyStr;
|
||||
if (!manager || !manager.folders[fId]) { return; }
|
||||
manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey);
|
||||
|
||||
manager.folders[fId].offline = newObj.offline;
|
||||
}));
|
||||
});
|
||||
// Remove from memory folders that have been deleted from the drive remotely
|
||||
|
|
|
@ -54,7 +54,13 @@ define([
|
|||
if (Utils.LocalStore.isLoggedIn()) { return; }
|
||||
Utils.LocalStore.setFSHash('');
|
||||
Utils.LocalStore.clearThumbnail();
|
||||
window.location.reload();
|
||||
try {
|
||||
Utils.Cache.clear(function () {
|
||||
window.location.reload();
|
||||
});
|
||||
} catch (e) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
|
||||
Cryptpad.userObjectCommand(data, cb);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@import (reference) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/markdown.less';
|
||||
|
||||
&.cp-app-file {
|
||||
|
||||
|
@ -47,6 +48,7 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
.mediatag_cryptpad();
|
||||
media-tag {
|
||||
img {
|
||||
max-width: 100%;
|
||||
|
@ -64,7 +66,49 @@
|
|||
}
|
||||
}
|
||||
|
||||
#cp-app-file-upload-form, #cp-app-file-download-form {
|
||||
#cp-app-file-download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
max-width: 90vw;
|
||||
height: 150px;
|
||||
width: ~"min(90vw, 600px)";
|
||||
.cp-app-file-progress-container {
|
||||
margin-top: 5px;
|
||||
height: 40px;
|
||||
font-size: 20px;
|
||||
border: 1px solid @colortheme_logo-2;
|
||||
background: white;
|
||||
color: @cryptpad_text_col;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
.cp-app-file-progress-dl {
|
||||
border-right: 1px solid @cryptpad_text_col;
|
||||
}
|
||||
.cp-app-file-progress-dl, .cp-app-file-progress-dc {
|
||||
width: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
}
|
||||
.cp-app-file-progress {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: @colortheme_logo-2;
|
||||
}
|
||||
}
|
||||
.cp-app-file-progress-txt {
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
#cp-app-file-upload-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
|
@ -156,7 +200,10 @@
|
|||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
&:empty {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,69 +48,6 @@ define([
|
|||
return new Blob(chunks);
|
||||
};
|
||||
|
||||
var concatBuffer = function (a, b) { // TODO make this not so ugly
|
||||
return new Uint8Array(slice(a).concat(slice(b)));
|
||||
};
|
||||
|
||||
var fetchMetadata = function (src, cb) {
|
||||
var done = false;
|
||||
var CB = function (err, res) {
|
||||
if (done) { return; }
|
||||
done = true;
|
||||
cb(err, res);
|
||||
};
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", src, true);
|
||||
xhr.setRequestHeader('Range', 'bytes=0-1');
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
xhr.onerror= function () { return CB('XHR_ERROR'); };
|
||||
xhr.onload = function () {
|
||||
if (/^4/.test('' + this.status)) { return CB('XHR_ERROR'); }
|
||||
var res = new Uint8Array(xhr.response);
|
||||
var size = decodePrefix(res);
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
|
||||
xhr2.open("GET", src, true);
|
||||
xhr2.setRequestHeader('Range', 'bytes=2-' + (size + 2));
|
||||
xhr2.responseType = 'arraybuffer';
|
||||
xhr2.onload = function () {
|
||||
if (/^4/.test('' + this.status)) { return CB('XHR_ERROR'); }
|
||||
var res2 = new Uint8Array(xhr2.response);
|
||||
var all = concatBuffer(res, res2);
|
||||
CB(void 0, all);
|
||||
};
|
||||
xhr2.send(null);
|
||||
};
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
var decryptMetadata = function (u8, key) {
|
||||
var prefix = u8.subarray(0, 2);
|
||||
var metadataLength = decodePrefix(prefix);
|
||||
|
||||
var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
|
||||
var metaChunk = Nacl.secretbox.open(metaBox, createNonce(), key);
|
||||
|
||||
try {
|
||||
return JSON.parse(Nacl.util.encodeUTF8(metaChunk));
|
||||
}
|
||||
catch (e) { return null; }
|
||||
};
|
||||
|
||||
var fetchDecryptedMetadata = function (src, key, cb) {
|
||||
if (typeof(src) !== 'string') {
|
||||
return window.setTimeout(function () {
|
||||
cb('NO_SOURCE');
|
||||
});
|
||||
}
|
||||
fetchMetadata(src, function (e, buffer) {
|
||||
if (e) { return cb(e); }
|
||||
cb(void 0, decryptMetadata(buffer, key));
|
||||
});
|
||||
};
|
||||
|
||||
var decrypt = function (u8, key, done, progress) {
|
||||
var MAX = u8.length;
|
||||
var _progress = function (offset) {
|
||||
|
@ -128,6 +65,11 @@ define([
|
|||
metadata: undefined,
|
||||
};
|
||||
|
||||
var cancelled = false;
|
||||
var cancel = function () {
|
||||
cancelled = true;
|
||||
};
|
||||
|
||||
var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
|
||||
|
||||
var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);
|
||||
|
@ -168,6 +110,7 @@ define([
|
|||
var chunks = [];
|
||||
|
||||
var again = function () {
|
||||
if (cancelled) { return; }
|
||||
takeChunk(function (e, plaintext) {
|
||||
if (e) {
|
||||
return setTimeout(function () {
|
||||
|
@ -188,6 +131,10 @@ define([
|
|||
};
|
||||
|
||||
again();
|
||||
|
||||
return {
|
||||
cancel: cancel
|
||||
};
|
||||
};
|
||||
|
||||
// metadata
|
||||
|
@ -258,8 +205,5 @@ define([
|
|||
encrypt: encrypt,
|
||||
joinChunks: joinChunks,
|
||||
computeEncryptedSize: computeEncryptedSize,
|
||||
decryptMetadata: decryptMetadata,
|
||||
fetchMetadata: fetchMetadata,
|
||||
fetchDecryptedMetadata: fetchDecryptedMetadata,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
<label for="cp-app-file-upfile" class="btn btn-primary cp-app-file-block unselectable" data-localization-title="upload_choose"
|
||||
data-localization="upload_choose"></label>
|
||||
</div>
|
||||
<div id="cp-app-file-download-form" style="display: none;">
|
||||
<input type="button" name="dl" id="cp-app-file-dlfile" class="cp-app-file-input" />
|
||||
<label for="cp-app-file-dlfile" class="btn btn-success cp-app-file-block unselectable" data-localization-title="download_button"><span data-localization="download_button"></span></label>
|
||||
<span class="cp-app-file-block" id="cp-app-file-dlprogress"></span>
|
||||
</div>
|
||||
<div id="cp-app-file-download-view" style="display: none;">
|
||||
<media-tag id="cp-app-file-view"></media-tag>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@ define([
|
|||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/hyperscript.js',
|
||||
'/customize/messages.js',
|
||||
|
||||
'/file/file-crypto.js',
|
||||
|
@ -29,6 +30,7 @@ define([
|
|||
Util,
|
||||
Hash,
|
||||
UI,
|
||||
h,
|
||||
Messages,
|
||||
FileCrypto,
|
||||
MediaTag)
|
||||
|
@ -37,18 +39,12 @@ define([
|
|||
var Nacl = window.nacl;
|
||||
|
||||
var APP = window.APP = {};
|
||||
MediaTag.setDefaultConfig('download', {
|
||||
text: Messages.download_mt_button
|
||||
});
|
||||
|
||||
var andThen = function (common) {
|
||||
var $appContainer = $('#cp-app-file-content');
|
||||
var $form = $('#cp-app-file-upload-form');
|
||||
var $dlform = $('#cp-app-file-download-form');
|
||||
var $dlview = $('#cp-app-file-download-view');
|
||||
var $label = $form.find('label');
|
||||
var $dllabel = $dlform.find('label span');
|
||||
var $progress = $('#cp-app-file-dlprogress');
|
||||
var $bar = $('.cp-toolbar-container');
|
||||
var $body = $('body');
|
||||
|
||||
|
@ -88,142 +84,86 @@ define([
|
|||
var toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||
|
||||
if (!uploadMode) {
|
||||
var hexFileName = secret.channel;
|
||||
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
var cryptKey = Nacl.util.encodeBase64(key);
|
||||
(function () {
|
||||
var hexFileName = secret.channel;
|
||||
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
var cryptKey = Nacl.util.encodeBase64(key);
|
||||
|
||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||
if (e) {
|
||||
if (e === 'XHR_ERROR') {
|
||||
return void UI.errorLoadingScreen(Messages.download_resourceNotAvailable, false, function () {
|
||||
common.gotoURL('/file/');
|
||||
});
|
||||
var $mt = $dlview.find('media-tag');
|
||||
$mt.attr('src', src);
|
||||
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
||||
$mt.css('transform', 'scale(2)');
|
||||
|
||||
var rightsideDisplayed = false;
|
||||
var metadataReceived = false;
|
||||
UI.removeLoadingScreen();
|
||||
$dlview.show();
|
||||
|
||||
MediaTag($mt[0]).on('complete', function (decrypted) {
|
||||
$mt.css('transform', '');
|
||||
if (!rightsideDisplayed) {
|
||||
toolbar.$drawer
|
||||
.append(common.createButton('export', true, {}, function () {
|
||||
saveAs(decrypted.content, decrypted.metadata.name);
|
||||
}));
|
||||
rightsideDisplayed = true;
|
||||
}
|
||||
return void console.error(e);
|
||||
}
|
||||
|
||||
// Add pad attributes when the file is saved in the drive
|
||||
Title.onTitleChange(function () {
|
||||
var owners = metadata.owners;
|
||||
if (owners) { common.setPadAttribute('owners', owners); }
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
});
|
||||
$(document).on('cpPadStored', function () {
|
||||
var owners = metadata.owners;
|
||||
if (owners) { common.setPadAttribute('owners', owners); }
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
});
|
||||
|
||||
// Save to the drive or update the acces time
|
||||
var title = document.title = metadata.name;
|
||||
Title.updateTitle(title || Title.defaultTitle);
|
||||
|
||||
var owners = metadata.owners;
|
||||
if (owners) {
|
||||
common.setPadAttribute('owners', owners);
|
||||
}
|
||||
if (metadata.type) {
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
}
|
||||
|
||||
toolbar.addElement(['pageTitle'], {
|
||||
pageTitle: title,
|
||||
title: Title.getTitleConfig(),
|
||||
});
|
||||
toolbar.$drawer.append(common.createButton('forget', true));
|
||||
toolbar.$drawer.append(common.createButton('properties', true));
|
||||
if (common.isLoggedIn()) {
|
||||
toolbar.$drawer.append(common.createButton('hashtag', true));
|
||||
}
|
||||
toolbar.$file.show();
|
||||
|
||||
var displayFile = function (ev, sizeMb, CB) {
|
||||
var called_back;
|
||||
var cb = function (e) {
|
||||
if (called_back) { return; }
|
||||
called_back = true;
|
||||
if (CB) { CB(e); }
|
||||
};
|
||||
|
||||
var $mt = $dlview.find('media-tag');
|
||||
$mt.attr('src', src);
|
||||
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
||||
|
||||
var rightsideDisplayed = false;
|
||||
|
||||
MediaTag($mt[0]).on('complete', function (decrypted) {
|
||||
$dlview.show();
|
||||
$dlform.hide();
|
||||
var $dlButton = $dlview.find('media-tag button');
|
||||
if (ev) { $dlButton.click(); }
|
||||
|
||||
if (!rightsideDisplayed) {
|
||||
toolbar.$drawer
|
||||
.append(common.createButton('export', true, {}, function () {
|
||||
saveAs(decrypted.content, decrypted.metadata.name);
|
||||
}));
|
||||
rightsideDisplayed = true;
|
||||
}
|
||||
|
||||
// make pdfs big
|
||||
var toolbarHeight = $('#cp-toolbar').height();
|
||||
var $another_iframe = $('media-tag iframe').css({
|
||||
'height': 'calc(100vh - ' + toolbarHeight + 'px)',
|
||||
'width': '100vw',
|
||||
'position': 'absolute',
|
||||
'bottom': 0,
|
||||
'left': 0,
|
||||
'border': 0
|
||||
});
|
||||
|
||||
if ($another_iframe.length) {
|
||||
$another_iframe.load(function () {
|
||||
cb();
|
||||
});
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}).on('progress', function (data) {
|
||||
var p = data.progress +'%';
|
||||
$progress.width(p);
|
||||
}).on('error', function (err) {
|
||||
console.error(err);
|
||||
// make pdfs big
|
||||
var toolbarHeight = $('#cp-toolbar').height();
|
||||
$('media-tag iframe').css({
|
||||
'height': 'calc(100vh - ' + toolbarHeight + 'px)',
|
||||
'width': '100vw',
|
||||
'position': 'absolute',
|
||||
'bottom': 0,
|
||||
'left': 0,
|
||||
'border': 0
|
||||
});
|
||||
}).on('metadata', function (metadata) {
|
||||
if (metadataReceived) { return; }
|
||||
metadataReceived = true;
|
||||
// Add pad attributes when the file is saved in the drive
|
||||
Title.onTitleChange(function () {
|
||||
var owners = metadata.owners;
|
||||
if (owners) { common.setPadAttribute('owners', owners); }
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
});
|
||||
$(document).on('cpPadStored', function () {
|
||||
var owners = metadata.owners;
|
||||
if (owners) { common.setPadAttribute('owners', owners); }
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
});
|
||||
};
|
||||
|
||||
var todoBigFile = function (sizeMb) {
|
||||
$dlform.show();
|
||||
UI.removeLoadingScreen();
|
||||
$dllabel.append($('<br>'));
|
||||
$dllabel.append(Util.fixHTML(metadata.name));
|
||||
// Save to the drive or update the acces time
|
||||
var title = document.title = metadata.name;
|
||||
Title.updateTitle(title || Title.defaultTitle);
|
||||
|
||||
// don't display the size if you don't know it.
|
||||
if (typeof(sizeM) === 'number') {
|
||||
$dllabel.append($('<br>'));
|
||||
$dllabel.append(Messages._getKey('formattedMB', [sizeMb]));
|
||||
var owners = metadata.owners;
|
||||
if (owners) {
|
||||
common.setPadAttribute('owners', owners);
|
||||
}
|
||||
var decrypting = false;
|
||||
var onClick = function (ev) {
|
||||
if (decrypting) { return; }
|
||||
decrypting = true;
|
||||
displayFile(ev, sizeMb, function (err) {
|
||||
$appContainer.css('background-color',
|
||||
common.getAppConfig().appBackgroundColor);
|
||||
if (err) { UI.alert(err); }
|
||||
});
|
||||
};
|
||||
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
|
||||
$dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick);
|
||||
};
|
||||
common.getFileSize(hexFileName, function (e, data) {
|
||||
if (e) {
|
||||
return void UI.errorLoadingScreen(e);
|
||||
if (metadata.type) {
|
||||
common.setPadAttribute('fileType', metadata.type);
|
||||
}
|
||||
var size = Util.bytesToMegabytes(data);
|
||||
return void todoBigFile(size);
|
||||
|
||||
toolbar.addElement(['pageTitle'], {
|
||||
pageTitle: title,
|
||||
title: Title.getTitleConfig(),
|
||||
});
|
||||
toolbar.$drawer.append(common.createButton('forget', true));
|
||||
toolbar.$drawer.append(common.createButton('properties', true));
|
||||
if (common.isLoggedIn()) {
|
||||
toolbar.$drawer.append(common.createButton('hashtag', true));
|
||||
}
|
||||
toolbar.$file.show();
|
||||
}).on('error', function (err) {
|
||||
$appContainer.css('background-color',
|
||||
common.getAppConfig().appBackgroundColor);
|
||||
UI.warn(Messages.error);
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ define([
|
|||
'/common/test.js',
|
||||
|
||||
'/bower_components/diff-dom/diffDOM.js',
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
|
||||
'css!/customize/src/print.css',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
|
@ -462,7 +463,9 @@ define([
|
|||
setTimeout(function() { // Just in case
|
||||
var tags = dom.querySelectorAll('media-tag:empty');
|
||||
Array.prototype.slice.call(tags).forEach(function(el) {
|
||||
MediaTag(el);
|
||||
var mediaObject = MediaTag(el, {
|
||||
body: dom
|
||||
});
|
||||
$(el).on('keydown', function(e) {
|
||||
if ([8, 46].indexOf(e.which) !== -1) {
|
||||
$(el).remove();
|
||||
|
@ -472,13 +475,17 @@ define([
|
|||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.type === 'childList') {
|
||||
var list_values = [].slice.call(el.children);
|
||||
var list_values = slice(el.children)
|
||||
.map(function (el) { return el.outerHTML; })
|
||||
.join('');
|
||||
mediaTagMap[el.getAttribute('src')] = list_values;
|
||||
if (mediaObject.complete) { observer.disconnect(); }
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(el, {
|
||||
attributes: false,
|
||||
subtree: true,
|
||||
childList: true,
|
||||
characterData: false
|
||||
});
|
||||
|
@ -491,9 +498,10 @@ define([
|
|||
Array.prototype.slice.call(tags).forEach(function(tag) {
|
||||
var src = tag.getAttribute('src');
|
||||
if (mediaTagMap[src]) {
|
||||
mediaTagMap[src].forEach(function(n) {
|
||||
tag.appendChild(n.cloneNode());
|
||||
});
|
||||
tag.innerHTML = mediaTagMap[src];
|
||||
/*mediaTagMap[src].forEach(function(n) {
|
||||
tag.appendChild(n.cloneNode(true));
|
||||
});*/
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1084,6 +1092,9 @@ define([
|
|||
border: Messages.pad_mediatagBorder,
|
||||
preview: Messages.pad_mediatagPreview,
|
||||
'import': Messages.pad_mediatagImport,
|
||||
download: Messages.download_mt_button,
|
||||
share: Messages.pad_mediatagShare,
|
||||
open: Messages.pad_mediatagOpen,
|
||||
options: Messages.pad_mediatagOptions
|
||||
};
|
||||
Ckeditor._commentsTranslations = {
|
||||
|
@ -1164,6 +1175,28 @@ define([
|
|||
editor.plugins.mediatag.import = function($mt) {
|
||||
framework._.sfCommon.importMediaTag($mt);
|
||||
};
|
||||
editor.plugins.mediatag.download = function($mt) {
|
||||
var media = Util.find($mt, [0, '_mediaObject']);
|
||||
if (!media) { return void console.error('no media'); }
|
||||
if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); }
|
||||
if (!(media && media._blob)) { return void console.error($mt); }
|
||||
window.saveAs(media._blob.content, media.name);
|
||||
};
|
||||
editor.plugins.mediatag.open = function($mt) {
|
||||
var hash = framework._.sfCommon.getHashFromMediaTag($mt);
|
||||
framework._.sfCommon.openURL(Hash.hashToHref(hash, 'file'));
|
||||
};
|
||||
editor.plugins.mediatag.share = function($mt) {
|
||||
var data = {
|
||||
file: true,
|
||||
pathname: '/file/',
|
||||
hashes: {
|
||||
fileHash: framework._.sfCommon.getHashFromMediaTag($mt)
|
||||
},
|
||||
title: Util.find($mt[0], ['_mediaObject', 'name']) || ''
|
||||
};
|
||||
framework._.sfCommon.getSframeChannel().event('EV_SHARE_OPEN', data);
|
||||
};
|
||||
Links.init(Ckeditor, editor);
|
||||
}).nThen(function() {
|
||||
// Move ckeditor parts to have a structure like the other apps
|
||||
|
|
|
@ -53,15 +53,57 @@
|
|||
editor.plugins.mediatag.import($mt);
|
||||
}
|
||||
});
|
||||
editor.addCommand('downloadMT', {
|
||||
exec: function (editor) {
|
||||
var w = targetWidget;
|
||||
targetWidget = undefined;
|
||||
var $mt = $(w.$).find('media-tag');
|
||||
editor.plugins.mediatag.download($mt);
|
||||
}
|
||||
});
|
||||
editor.addCommand('openMT', {
|
||||
exec: function (editor) {
|
||||
var w = targetWidget;
|
||||
targetWidget = undefined;
|
||||
var $mt = $(w.$).find('media-tag');
|
||||
editor.plugins.mediatag.open($mt);
|
||||
}
|
||||
});
|
||||
editor.addCommand('shareMT', {
|
||||
exec: function (editor) {
|
||||
var w = targetWidget;
|
||||
targetWidget = undefined;
|
||||
var $mt = $(w.$).find('media-tag');
|
||||
editor.plugins.mediatag.share($mt);
|
||||
}
|
||||
});
|
||||
|
||||
if (editor.addMenuItems) {
|
||||
editor.addMenuGroup('mediatag');
|
||||
editor.addMenuItem('open', {
|
||||
label: Messages.open,
|
||||
icon: 'iframe',
|
||||
command: 'openMT',
|
||||
group: 'mediatag'
|
||||
});
|
||||
editor.addMenuItem('share', {
|
||||
label: Messages.share,
|
||||
icon: 'link',
|
||||
command: 'shareMT',
|
||||
group: 'mediatag'
|
||||
});
|
||||
editor.addMenuItem('importMediatag', {
|
||||
label: Messages.import,
|
||||
icon: 'save',
|
||||
command: 'importMediatag',
|
||||
group: 'mediatag'
|
||||
});
|
||||
editor.addMenuItem('download', {
|
||||
label: Messages.download,
|
||||
icon: 'save',
|
||||
command: 'downloadMT',
|
||||
group: 'mediatag'
|
||||
});
|
||||
editor.addMenuItem('mediatag', {
|
||||
label: Messages.options,
|
||||
icon: 'image',
|
||||
|
@ -76,6 +118,9 @@
|
|||
targetWidget = element;
|
||||
return {
|
||||
mediatag: CKEDITOR.TRISTATE_OFF,
|
||||
open: CKEDITOR.TRISTATE_OFF,
|
||||
share: CKEDITOR.TRISTATE_OFF,
|
||||
download: CKEDITOR.TRISTATE_OFF,
|
||||
importMediatag: CKEDITOR.TRISTATE_OFF,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -52,10 +52,10 @@ define([
|
|||
: Share.getShareModal;
|
||||
f(common, {
|
||||
origin: priv.origin,
|
||||
pathname: priv.pathname,
|
||||
password: priv.password,
|
||||
isTemplate: priv.isTemplate,
|
||||
hashes: priv.hashes,
|
||||
pathname: data.pathname || priv.pathname,
|
||||
password: data.hashes ? '' : priv.password,
|
||||
isTemplate: data.hashes ? false : priv.isTemplate,
|
||||
hashes: data.hashes || priv.hashes,
|
||||
common: common,
|
||||
title: data.title,
|
||||
versionHash: data.versionHash,
|
||||
|
@ -64,8 +64,8 @@ define([
|
|||
hideIframe();
|
||||
},
|
||||
fileData: {
|
||||
hash: priv.hashes.fileHash,
|
||||
password: priv.password
|
||||
hash: (data.hashes && data.hashes.fileHash) || priv.hashes.fileHash,
|
||||
password: data.hashes ? '' : priv.password
|
||||
}
|
||||
}, function (e, modal) {
|
||||
if (e) { console.error(e); }
|
||||
|
|
|
@ -33,7 +33,7 @@ define([
|
|||
// loading screen setup.
|
||||
var done = waitFor();
|
||||
var onMsg = function (msg) {
|
||||
var data = JSON.parse(msg.data);
|
||||
var data = typeof(msg.data) === "object" ? msg.data : JSON.parse(msg.data);
|
||||
if (data.q !== 'READY') { return; }
|
||||
window.removeEventListener('message', onMsg);
|
||||
var _done = done;
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
margin-right: 100%;
|
||||
}
|
||||
}
|
||||
& > .fa {
|
||||
align-self: center;
|
||||
margin-right: -16px;
|
||||
}
|
||||
}
|
||||
.cp-settings-info-block {
|
||||
[type="text"] {
|
||||
|
|
|
@ -51,7 +51,7 @@ define([
|
|||
'cp-settings-info-block',
|
||||
'cp-settings-displayname',
|
||||
'cp-settings-language-selector',
|
||||
'cp-settings-resettips',
|
||||
'cp-settings-mediatag-size',
|
||||
'cp-settings-change-password',
|
||||
'cp-settings-delete'
|
||||
],
|
||||
|
@ -62,6 +62,7 @@ define([
|
|||
'cp-settings-userfeedback',
|
||||
],
|
||||
'drive': [
|
||||
'cp-settings-resettips',
|
||||
'cp-settings-drive-duplicate',
|
||||
'cp-settings-thumbnails',
|
||||
'cp-settings-drive-backup',
|
||||
|
@ -576,6 +577,57 @@ define([
|
|||
cb(form);
|
||||
}, true);
|
||||
|
||||
makeBlock('mediatag-size', function(cb) {
|
||||
var $inputBlock = $('<div>', {
|
||||
'class': 'cp-sidebarlayout-input-block',
|
||||
});
|
||||
|
||||
var spinner;
|
||||
var $input = $('<input>', {
|
||||
'min': -1,
|
||||
'max': 1000,
|
||||
type: 'number',
|
||||
}).appendTo($inputBlock);
|
||||
|
||||
var oldVal;
|
||||
|
||||
var todo = function () {
|
||||
var val = parseInt($input.val());
|
||||
if (val === oldVal) { return; }
|
||||
if (typeof(val) !== 'number') { return UI.warn(Messages.error); }
|
||||
spinner.spin();
|
||||
common.setAttribute(['general', 'mediatag-size'], val, function (err) {
|
||||
if (err) {
|
||||
spinner.hide();
|
||||
console.error(err);
|
||||
return UI.warn(Messages.error);
|
||||
}
|
||||
spinner.done();
|
||||
UI.log(Messages.saved);
|
||||
});
|
||||
};
|
||||
var $save = $(h('button.btn.btn-primary', Messages.settings_save)).appendTo($inputBlock);
|
||||
spinner = UI.makeSpinner($inputBlock);
|
||||
|
||||
$save.click(todo);
|
||||
$input.on('keyup', function(e) {
|
||||
if (e.which === 13) { todo(); }
|
||||
});
|
||||
|
||||
common.getAttribute(['general', 'mediatag-size'], function(e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (typeof(val) !== 'number') {
|
||||
oldVal = 5;
|
||||
$input.val(5);
|
||||
} else {
|
||||
oldVal = val;
|
||||
$input.val(val);
|
||||
}
|
||||
});
|
||||
|
||||
cb($inputBlock);
|
||||
}, true);
|
||||
|
||||
// Security
|
||||
|
||||
makeBlock('safe-links', function(cb) {
|
||||
|
@ -777,7 +829,7 @@ define([
|
|||
Feedback.send('FULL_DRIVE_EXPORT_COMPLETE');
|
||||
saveAs(blob, filename);
|
||||
}, errors);
|
||||
}, ui.update);
|
||||
}, ui.update, common.getCache());
|
||||
ui.onCancel(function() {
|
||||
ui.close();
|
||||
bu.stop();
|
||||
|
|
|
@ -397,6 +397,7 @@ define([
|
|||
|
||||
var fmConfig = {
|
||||
body: $('body'),
|
||||
noStore: true, // Don't store attachments into our drive
|
||||
onUploaded: function (ev, data) {
|
||||
if (ev.callback) {
|
||||
ev.callback(data);
|
||||
|
|
|
@ -46,7 +46,9 @@ define([
|
|||
Backup,
|
||||
Messages)
|
||||
{
|
||||
var APP = {};
|
||||
var APP = {
|
||||
teams: {}
|
||||
};
|
||||
var driveAPP = {};
|
||||
var saveAs = window.saveAs;
|
||||
//var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName;
|
||||
|
@ -91,6 +93,8 @@ define([
|
|||
var readOnly = !secret.keys.editKeyStr;
|
||||
if (!manager || !manager.folders[fId]) { return; }
|
||||
manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey);
|
||||
|
||||
manager.folders[fId].offline = newObj.offline;
|
||||
}));
|
||||
});
|
||||
// Remove from memory folders that have been deleted from the drive remotely
|
||||
|
@ -211,6 +215,11 @@ define([
|
|||
if (obj && obj.error) {
|
||||
return void UI.warn(Messages.error);
|
||||
}
|
||||
|
||||
// Refresh offline state
|
||||
APP.teams[APP.team] = APP.teams[APP.team] || {};
|
||||
APP.teams[APP.team].offline = obj.offline;
|
||||
|
||||
common.displayAvatar($avatar, obj.avatar, obj.name);
|
||||
$category.append($avatar);
|
||||
$avatar.append(h('span.cp-sidebarlayout-category-name', obj.name));
|
||||
|
@ -333,6 +342,11 @@ define([
|
|||
});
|
||||
APP.drive = drive;
|
||||
driveAPP.refresh = drive.refresh;
|
||||
|
||||
if (APP.teams[id] && APP.teams[id].offline) {
|
||||
setEditable(false);
|
||||
drive.refresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -406,7 +420,15 @@ define([
|
|||
|
||||
content.push(h('h3', Messages.team_listTitle + ' ' + slots));
|
||||
|
||||
APP.teams = {};
|
||||
|
||||
keys.forEach(function (id) {
|
||||
if (!obj[id].empty) {
|
||||
APP.teams[id] = {
|
||||
offline: obj[id] && obj[id].offline
|
||||
};
|
||||
}
|
||||
|
||||
var team = obj[id];
|
||||
if (team.empty) {
|
||||
list.push(h('div.cp-team-list-team.empty', [
|
||||
|
@ -1049,7 +1071,7 @@ define([
|
|||
Feedback.send('FULL_TEAMDRIVE_EXPORT_COMPLETE');
|
||||
saveAs(blob, filename);
|
||||
}, errors);
|
||||
}, ui.update);
|
||||
}, ui.update, common.getCache);
|
||||
ui.onCancel(function() {
|
||||
ui.close();
|
||||
bu.stop();
|
||||
|
@ -1433,13 +1455,15 @@ define([
|
|||
}
|
||||
});
|
||||
|
||||
var onDisconnect = function (noAlert) {
|
||||
var onDisconnect = function (teamId) {
|
||||
if (APP.team && teamId && APP.team !== teamId) { return; }
|
||||
setEditable(false);
|
||||
if (APP.team && driveAPP.refresh) { driveAPP.refresh(); }
|
||||
toolbar.failed();
|
||||
if (!noAlert) { UIElements.disconnectAlert(); }
|
||||
UIElements.disconnectAlert();
|
||||
};
|
||||
var onReconnect = function () {
|
||||
var onReconnect = function (teamId) {
|
||||
if (APP.team && teamId && APP.team !== teamId) { return; }
|
||||
setEditable(true);
|
||||
if (APP.team && driveAPP.refresh) { driveAPP.refresh(); }
|
||||
toolbar.reconnecting();
|
||||
|
@ -1449,11 +1473,17 @@ define([
|
|||
sframeChan.on('EV_DRIVE_LOG', function (msg) {
|
||||
UI.log(msg);
|
||||
});
|
||||
sframeChan.on('EV_NETWORK_DISCONNECT', function () {
|
||||
onDisconnect();
|
||||
sframeChan.on('EV_NETWORK_DISCONNECT', function (teamId) {
|
||||
onDisconnect(teamId);
|
||||
if (teamId && APP.teams[teamId]) {
|
||||
APP.teams[teamId].offline = true;
|
||||
}
|
||||
});
|
||||
sframeChan.on('EV_NETWORK_RECONNECT', function () {
|
||||
onReconnect();
|
||||
sframeChan.on('EV_NETWORK_RECONNECT', function (teamId) {
|
||||
onReconnect(teamId);
|
||||
if (teamId && APP.teams[teamId]) {
|
||||
APP.teams[teamId].offline = false;
|
||||
}
|
||||
});
|
||||
common.onLogout(function () { setEditable(false); });
|
||||
});
|
||||
|
|
|
@ -55,10 +55,10 @@ define([
|
|||
sframeChan.event('EV_'+obj.data.ev, obj.data.data);
|
||||
}
|
||||
if (obj.data.ev === 'NETWORK_RECONNECT') {
|
||||
sframeChan.event('EV_NETWORK_RECONNECT');
|
||||
sframeChan.event('EV_NETWORK_RECONNECT', obj.data.data);
|
||||
}
|
||||
if (obj.data.ev === 'NETWORK_DISCONNECT') {
|
||||
sframeChan.event('EV_NETWORK_DISCONNECT');
|
||||
sframeChan.event('EV_NETWORK_DISCONNECT', obj.data.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -331,10 +331,11 @@ define([
|
|||
APP.FM.handleFile(blob);
|
||||
});
|
||||
};
|
||||
var MAX_IMAGE_SIZE = 1 * 1024 * 1024; // 1 MB
|
||||
var maxSizeStr = Messages._getKey('formattedMB', [Util.bytesToMegabytes(MAX_IMAGE_SIZE)]);
|
||||
var addImageToCanvas = function (img) {
|
||||
// 1 MB maximum
|
||||
if (img.src && img.src.length > 1 * 1024 * 1024) {
|
||||
UI.warn(Messages.upload_tooLargeBrief);
|
||||
if (img.src && img.src.length > MAX_IMAGE_SIZE) {
|
||||
UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr]));
|
||||
return;
|
||||
}
|
||||
var w = img.width;
|
||||
|
@ -356,8 +357,8 @@ define([
|
|||
var file = e.target.files[0];
|
||||
var reader = new FileReader();
|
||||
// 1 MB maximum
|
||||
if (file.size > 1 * 1024 * 1024) {
|
||||
UI.warn(Messages.upload_tooLargeBrief);
|
||||
if (file.size > MAX_IMAGE_SIZE) {
|
||||
UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr]));
|
||||
return;
|
||||
}
|
||||
reader.onload = function () {
|
||||
|
|
Loading…
Reference in New Issue