mirror of
http://git.whoc.org.uk/git/password-manager.git
synced 2025-10-24 01:07:35 +02:00
Implemented Attachments in client
This commit is contained in:
@@ -703,6 +703,91 @@ MochiKit.Base.update(Clipperz.Async, {
|
||||
|
||||
'clearResult': function () {},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'doXHR': function (url, opts) {
|
||||
// ALMOST COPYCAT OF MochiKit version
|
||||
/*
|
||||
Work around a Firefox bug by dealing with XHR during
|
||||
the next event loop iteration. Maybe it's this one:
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=249843
|
||||
*/
|
||||
return MochiKit.Async.callLater(0, Clipperz.Async._doXHR, url, opts);
|
||||
},
|
||||
|
||||
_doXHR: function (url, opts) {
|
||||
// ALMOST COPYCAT OF MochiKit version
|
||||
var m = MochiKit.Base;
|
||||
opts = m.update({
|
||||
method: 'GET',
|
||||
sendContent: ''
|
||||
/*
|
||||
queryString: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
headers: undefined,
|
||||
mimeType: undefined,
|
||||
responseType: undefined,
|
||||
withCredentials: undefined
|
||||
*/
|
||||
}, opts);
|
||||
|
||||
var self = MochiKit.Async;
|
||||
var req = self.getXMLHttpRequest();
|
||||
|
||||
// Extra options not handled by MochiKit
|
||||
if (req.upload && opts.uploadProgress) {
|
||||
req.upload.onprogress = opts.uploadProgress;
|
||||
}
|
||||
if (opts.downloadProgress) {
|
||||
req.onprogress = opts.downloadProgress;
|
||||
}
|
||||
|
||||
if (opts.queryString) {
|
||||
var qs = m.queryString(opts.queryString);
|
||||
if (qs) {
|
||||
url += "?" + qs;
|
||||
}
|
||||
}
|
||||
|
||||
// Safari will send undefined:undefined, so we have to check.
|
||||
// We can't use apply, since the function is native.
|
||||
if ('username' in opts) {
|
||||
req.open(opts.method, url, true, opts.username, opts.password);
|
||||
} else {
|
||||
req.open(opts.method, url, true);
|
||||
}
|
||||
|
||||
if (req.overrideMimeType && opts.mimeType) {
|
||||
req.overrideMimeType(opts.mimeType);
|
||||
}
|
||||
|
||||
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
if (opts.headers) {
|
||||
var headers = opts.headers;
|
||||
if (!m.isArrayLike(headers)) {
|
||||
headers = m.items(headers);
|
||||
}
|
||||
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
var header = headers[i];
|
||||
var name = header[0];
|
||||
var value = header[1];
|
||||
req.setRequestHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if ("responseType" in opts && "responseType" in req) {
|
||||
req.responseType = opts.responseType;
|
||||
}
|
||||
|
||||
if (opts.withCredentials) {
|
||||
req.withCredentials = 'true';
|
||||
}
|
||||
|
||||
return self.sendXMLHttpRequest(req, opts.sendContent);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
||||
@@ -36,6 +36,44 @@ if (typeof(Clipperz.Crypto.AES_2) == 'undefined') { Clipperz.Crypto.AES_2 = {};
|
||||
|
||||
//#############################################################################
|
||||
|
||||
Clipperz.Crypto.AES_2.EncryptionStreamingExecutionContext = function(args) {
|
||||
args = args || {};
|
||||
|
||||
this._key = args.key || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
this._nonce = args.nonce || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Clipperz.Crypto.AES_2.EncryptionStreamingExecutionContext.prototype = MochiKit.Base.update(null, {
|
||||
key: function () {
|
||||
return this._key;
|
||||
},
|
||||
|
||||
nonce: function () {
|
||||
return this._nonce;
|
||||
},
|
||||
|
||||
deferredProcessBlock: function (someData) {
|
||||
var deferredResult
|
||||
var executionContext;
|
||||
|
||||
executionContext = new Clipperz.Crypto.AES_2.DeferredExecutionContext({key:this.key(), message:someData, nonce:this.nonce()});
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("AES.deferredDecrypt", {trace:false});
|
||||
deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncryptBlocks);
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function (anExecutionContext) {
|
||||
this._nonce = new Clipperz.ByteArray(anExecutionContext.nonceArray());
|
||||
return anExecutionContext.result();
|
||||
}, this));
|
||||
deferredResult.callback(executionContext);
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
});
|
||||
|
||||
//#############################################################################
|
||||
|
||||
Clipperz.Crypto.AES_2.DeferredExecutionContext = function(args) {
|
||||
args = args || {};
|
||||
|
||||
@@ -110,8 +148,9 @@ Clipperz.Crypto.AES_2.DeferredExecutionContext.prototype = MochiKit.Base.update(
|
||||
//var originalChunks = this._elaborationChunks;
|
||||
if (anElapsedTime > 0) {
|
||||
this._elaborationChunks = Math.round(this._elaborationChunks * ((anElapsedTime + 1000)/(anElapsedTime * 2)));
|
||||
//console.log("tuneExecutionParameter", this._elaborationChunks);
|
||||
}
|
||||
//Clipperz.log("tuneExecutionParameters - elapsedTime: " + anElapsedTime + /*originalChunks,*/ " chunks # " + this._elaborationChunks + " [" + this._executionStep + " / " + this._messageLength + "]");
|
||||
//console.log("tuneExecutionParameters - elapsedTime: " + anElapsedTime + /*originalChunks,*/ " chunks # " + this._elaborationChunks + " [" + this._executionStep + " / " + this._messageLength + "]");
|
||||
},
|
||||
|
||||
'pause': function(aValue) {
|
||||
@@ -718,7 +757,8 @@ MochiKit.Base.update(Clipperz.Crypto.AES_2, {
|
||||
self = Clipperz.Crypto.AES_2;
|
||||
startTime = new Date();
|
||||
blockSize = 128/8;
|
||||
messageLength = anExecutionContext.messageArray().length;
|
||||
// messageLength = anExecutionContext.messageArray().length;
|
||||
messageLength = anExecutionContext.messageLength();
|
||||
nonce = anExecutionContext.nonceArray();
|
||||
result = anExecutionContext.resultArray();
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ refer to http://www.clipperz.com.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
|
||||
if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
|
||||
|
||||
@@ -126,6 +128,16 @@ Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, {
|
||||
throw Clipperz.Base.exception.AbstractMethod;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'uploadAttachment': function(someArguments, aProgressCallback) {
|
||||
throw Clipperz.Base.exception.AbstractMethod;
|
||||
},
|
||||
|
||||
'downloadAttachment': function(someArguments, aProgressCallback) {
|
||||
throw Clipperz.Base.exception.AbstractMethod;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'sharedSecret': function () {
|
||||
@@ -433,7 +445,7 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'message': function(aMessageName, someParameters) {
|
||||
'message': function(aMessageName, someParameters, someOptionalParameters) {
|
||||
var args;
|
||||
var parameters;
|
||||
|
||||
@@ -449,16 +461,16 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
|
||||
parameters: parameters
|
||||
}
|
||||
|
||||
return this.sendMessage(args);
|
||||
return this.sendMessage(args, someOptionalParameters);
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'sendMessage': function(someArguments) {
|
||||
'sendMessage': function(someArguments, someOptionalParameters) {
|
||||
var deferredResult;
|
||||
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false});
|
||||
deferredResult.addMethod(this.proxy(), 'message', someArguments);
|
||||
deferredResult.addMethod(this.proxy(), 'message', someArguments, someOptionalParameters);
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function(res) {
|
||||
if (typeof(res['lock']) != 'undefined') {
|
||||
this.setServerLockValue(res['lock']);
|
||||
@@ -498,6 +510,30 @@ Clipperz.log("<<< Connection.messageExceptionHandler")
|
||||
|
||||
//=========================================================================
|
||||
|
||||
// 'uploadAttachment': function(someArguments, aProgressCallback) {
|
||||
// return this.message('uploadAttachment', someArguments, {'uploadProgressCallback': aProgressCallback});
|
||||
// },
|
||||
|
||||
'uploadAttachment': function(someArguments, aProgressCallback) {
|
||||
return Clipperz.Async.callbacks("Connction.uploadAttachment", [
|
||||
MochiKit.Base.method(this, 'message', 'knock', {'requestType':'MESSAGE'}),
|
||||
MochiKit.Base.method(this.proxy(), 'uploadAttachment', someArguments, aProgressCallback, this.sharedSecret()),
|
||||
], {trace:false});
|
||||
|
||||
// return this.proxy().uploadAttachment(someArguments, aProgressCallback, this.sharedSecret());
|
||||
},
|
||||
|
||||
'downloadAttachment': function(someArguments, aProgressCallback) {
|
||||
return Clipperz.Async.callbacks("Connction.uploadAttachment", [
|
||||
MochiKit.Base.method(this, 'message', 'knock', {'requestType':'MESSAGE'}),
|
||||
MochiKit.Base.method(this.proxy(), 'downloadAttachment', someArguments, aProgressCallback, this.sharedSecret()),
|
||||
], {trace:false});
|
||||
|
||||
// return this.proxy().downloadAttachment(someArguments, aProgressCallback, this.sharedSecret());
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'reestablishConnection': function(anOriginalMessageArguments) {
|
||||
var deferredResult;
|
||||
|
||||
|
||||
@@ -434,7 +434,7 @@ MochiKit.Base.update(Clipperz.PM.Crypto, {
|
||||
deferredResult.addErrback(function(anError) {
|
||||
Clipperz.logError("Error while decrypting data [4]");
|
||||
throw Clipperz.Crypto.Base.exception.CorruptedMessage;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
deferredResult.addCallback(function() {
|
||||
return null;
|
||||
|
||||
374
frontend/delta/js/Clipperz/PM/DataModel/Attachment.js
Normal file
374
frontend/delta/js/Clipperz/PM/DataModel/Attachment.js
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
Clipperz.Base.module('Clipperz.PM.DataModel');
|
||||
|
||||
Clipperz.PM.DataModel.Attachment = function(args) {
|
||||
args = args || {};
|
||||
|
||||
Clipperz.PM.DataModel.Attachment.superclass.constructor.apply(this, arguments);
|
||||
|
||||
this._reference = args.reference
|
||||
|| Clipperz.PM.Crypto.randomKey();
|
||||
this._record = args.record
|
||||
|| Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
|
||||
// this._retrieveIndexDataFunction = args.retrieveIndexDataFunction
|
||||
// || this.record().retrieveAttachmentIndexDataFunction()
|
||||
// || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
// this._setIndexDataFunction = args.setIndexDataFunction
|
||||
// || this.record().setAttachmentIndexDataFunction()
|
||||
// || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
// this._removeIndexDataFunction = args.removeIndexDataFunction
|
||||
// || this.record().removeAttachmentIndexDataFunction()
|
||||
// || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
|
||||
// this.setFile(args.file);
|
||||
|
||||
this._transientState = null;
|
||||
|
||||
this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
|
||||
|
||||
this.record().bindAttachment(this);
|
||||
|
||||
if (this._isBrandNew) {
|
||||
this.setKey(Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(256/8));
|
||||
this.setNonce(Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(128/8));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
|
||||
|
||||
'toString': function() {
|
||||
return "Attachment (" + this.reference() + ")";
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'reference': function () {
|
||||
return this._reference;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'record': function () {
|
||||
return this._record;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'isBrandNew': function () {
|
||||
return this._isBrandNew;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'removeIndexDataFunction': function () {
|
||||
return this._removeIndexDataFunction;
|
||||
},
|
||||
|
||||
'remove': function () {
|
||||
return Clipperz.Async.callbacks("DirectLogin.remove", [
|
||||
MochiKit.Base.partial(this.removeIndexDataFunction(), this.reference()),
|
||||
MochiKit.Base.method(this.record(), 'removeAttachment', this)
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'file': function () {
|
||||
// return this.getValue('name');
|
||||
return MochiKit.Async.succeed(this._file);
|
||||
},
|
||||
|
||||
'setFile': function (aFile) {
|
||||
this._file = aFile || null;
|
||||
|
||||
/* These ones will disappear when the application is closed */
|
||||
this._name = aFile ? aFile['name'] : null;
|
||||
this._contentType = aFile ? aFile['type'] : null;
|
||||
this._size = aFile ? aFile['size'] : null;
|
||||
|
||||
/* These ones will be saved in the Record */
|
||||
return Clipperz.Async.callbacks("Attachment.setFile", [
|
||||
MochiKit.Base.method(this, 'setValue', 'name', aFile['name']),
|
||||
MochiKit.Base.method(this, 'setValue', 'contentType', aFile['type']),
|
||||
MochiKit.Base.method(this, 'setValue', 'size', aFile['size']),
|
||||
|
||||
MochiKit.Base.partial(MochiKit.Async.succeed, this),
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'name': function () {
|
||||
return this.getValue('name');
|
||||
},
|
||||
|
||||
'contentType': function () {
|
||||
return this.getValue('contentType');
|
||||
},
|
||||
|
||||
'size': function () {
|
||||
return this.getValue('size');
|
||||
},
|
||||
|
||||
'metadata': function () {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Attachment.metadata [collect results]", {trace:false});
|
||||
deferredResult.collectResults({
|
||||
'name': MochiKit.Base.method(this, 'name'),
|
||||
'type': MochiKit.Base.method(this, 'contentType'),
|
||||
'size': MochiKit.Base.method(this, 'size'),
|
||||
}, {trace:false});
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
|
||||
// return {
|
||||
// 'name': this._name,
|
||||
// 'type': this._type,
|
||||
// 'size': this._size,
|
||||
// }
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'key': function () {
|
||||
var byteArray;
|
||||
|
||||
byteArray = new Clipperz.ByteArray();
|
||||
|
||||
return Clipperz.Async.callbacks("Attachment.key", [
|
||||
MochiKit.Base.method(this, 'getValue', 'key'),
|
||||
MochiKit.Base.method(byteArray, 'appendBase64String'),
|
||||
function(aByteArray) { return new Uint8Array(aByteArray.arrayValues()); }
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
// 'key': function () {
|
||||
// var result;
|
||||
|
||||
// result = new Clipperz.ByteArray();
|
||||
|
||||
// return Clipperz.Async.callbacks("Attachment.key", [
|
||||
// MochiKit.Base.method(this, 'getValue', 'key'),
|
||||
// MochiKit.Base.method(result, 'appendBase64String'),
|
||||
// function(aByteArray) { return new Clipperz.Crypto.AES.Key({key: aByteArray}); }
|
||||
// ], {trace:false});
|
||||
// },
|
||||
|
||||
'nonce': function () {
|
||||
var byteArray;
|
||||
|
||||
byteArray = new Clipperz.ByteArray();
|
||||
|
||||
return Clipperz.Async.callbacks("Attachment.nonce", [
|
||||
MochiKit.Base.method(this, 'getValue', 'nonce'),
|
||||
MochiKit.Base.method(byteArray, 'appendBase64String'),
|
||||
function(aByteArray) { return new Uint8Array(aByteArray.arrayValues()); }
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
// 'nonce': function () {
|
||||
// var result;
|
||||
|
||||
// result = new Clipperz.ByteArray();
|
||||
|
||||
// return Clipperz.Async.callbacks("Attachment.nonce", [
|
||||
// MochiKit.Base.method(this, 'getValue', 'nonce'),
|
||||
// MochiKit.Base.method(result, 'appendBase64String')
|
||||
// ], {trace:false});
|
||||
// },
|
||||
|
||||
'setKey': function (aByteArray) {
|
||||
this.setValue('key', aByteArray.toBase64String());
|
||||
},
|
||||
|
||||
// 'setKey': function (aKey) {
|
||||
// var byteArray = aKey.key();
|
||||
// var serializedData = byteArray.toBase64String();
|
||||
|
||||
// this.setValue('key', serializedData);
|
||||
// },
|
||||
|
||||
'setNonce': function (aByteArray) {
|
||||
this.setValue('nonce', aByteArray.toBase64String());
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'serializedData': function () {
|
||||
return Clipperz.Async.collectResults("Attachment.serializedData", {
|
||||
'name': MochiKit.Base.method(this, 'name'),
|
||||
'contentType': MochiKit.Base.method(this, 'contentType'),
|
||||
'size': MochiKit.Base.method(this, 'size'),
|
||||
}, {trace:false})()
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'hasPendingChanges': function () {
|
||||
var result;
|
||||
// var deferredResult;
|
||||
|
||||
result = false;
|
||||
result = result || this.isBrandNew();
|
||||
|
||||
return MochiKit.Async.succeed(result);
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'revertChanges': function () {
|
||||
return MochiKit.Async.succeed();
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'transientState': function () {
|
||||
if (this._transientState == null) {
|
||||
this._transientState = {}
|
||||
}
|
||||
|
||||
return this._transientState;
|
||||
},
|
||||
|
||||
'resetTransientState': function (isCommitting) {
|
||||
this._transientState = null;
|
||||
},
|
||||
|
||||
'commitTransientState': function (isCommitting) {
|
||||
this._transientState = null;
|
||||
this._isBrandNew = false;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'actualKey': function (aValueKey) {
|
||||
var actualKey;
|
||||
|
||||
actualKey = 'attachments' + '.' + this.reference();
|
||||
if (aValueKey != '') {
|
||||
actualKey = actualKey + '.' + aValueKey;
|
||||
}
|
||||
|
||||
return actualKey;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'getValue': function (aValueKey) {
|
||||
return this.record().getValue(this.actualKey(aValueKey));
|
||||
},
|
||||
|
||||
'setValue': function (aValueKey, aValue) {
|
||||
return Clipperz.Async.callbacks("Attachment.setValue", [
|
||||
// MochiKit.Base.method(this, 'getValue', ''),
|
||||
// MochiKit.Base.bind(function (aValue) {
|
||||
// if (this.originalConfiguration() == null) {
|
||||
// this.setOriginalConfiguration(aValue);
|
||||
// }
|
||||
// }, this),
|
||||
MochiKit.Base.method(this.record(), 'setValue', this.actualKey(aValueKey), aValue)
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
'removeValue': function (aValueKey) {
|
||||
return this.record().removeValue(this.actualKey(aValueKey));
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'content': function () {
|
||||
// return this.serializedData();
|
||||
// return MochiKit.Async.succeed(this);
|
||||
|
||||
var deferredResult;
|
||||
var fieldValues;
|
||||
|
||||
fieldValues = {};
|
||||
deferredResult = new Clipperz.Async.Deferred("Attachment.content", {trace:false});
|
||||
deferredResult.addMethod(this, 'reference');
|
||||
deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'deleteAllCleanTextData': function () {
|
||||
this._name = null;
|
||||
this._contentType = null;
|
||||
this._size = null;
|
||||
|
||||
this.resetTransientState();
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'hasAnyCleanTextData': function () {
|
||||
var result;
|
||||
|
||||
result = false;
|
||||
|
||||
result = result || (this._name != null);
|
||||
result = result || (this._contentType != null);
|
||||
result = result || (this._size != null);
|
||||
result = result || (MochiKit.Base.keys(this.transientState()).length != 0);
|
||||
|
||||
return MochiKit.Async.succeed(result);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
||||
Clipperz.PM.DataModel.Attachment.MAX_ATTACHMENT_SIZE = 50*1024*1024;
|
||||
|
||||
Clipperz.PM.DataModel.Attachment.contentTypeIcon = function (aContentType) {
|
||||
var result;
|
||||
|
||||
result = 'other file';
|
||||
|
||||
if (aContentType == "application/pdf") {
|
||||
result = 'pdf file';
|
||||
} else if (aContentType.indexOf('image/') == 0) {
|
||||
result = 'image file';
|
||||
} else if (aContentType.indexOf('model/') == 0) {
|
||||
result = 'other file';
|
||||
} else if (aContentType.indexOf('audio/') == 0) {
|
||||
result = 'audio file';
|
||||
} else if (aContentType.indexOf('text/') == 0) {
|
||||
result = 'text file';
|
||||
} else if (aContentType.indexOf('video/') == 0) {
|
||||
result = 'video file';
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -55,7 +55,7 @@ Clipperz.PM.DataModel.DirectLogin = function(args) {
|
||||
|
||||
this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
|
||||
|
||||
this.record().addDirectLogin(this);
|
||||
this.record().bindDirectLogin(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1018,7 +1018,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.DirectLogin, Object, {
|
||||
'removeValue': function (aValueKey) {
|
||||
// return this.record().removeValue(this.actualKey(aValueKey));
|
||||
|
||||
return Clipperz.Async.callbacks("DirectLogin.setValue", [
|
||||
return Clipperz.Async.callbacks("DirectLogin.removeValue", [
|
||||
MochiKit.Base.method(this, 'originalConfiguration'),
|
||||
Clipperz.Async.deferredIf("originalConfiguration has been set", [
|
||||
], [
|
||||
|
||||
@@ -192,7 +192,7 @@ Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(nul
|
||||
innerDeferredResult = MochiKit.Async.succeed(this._remoteData);
|
||||
} else {
|
||||
innerDeferredResult = Clipperz.Async.callbacks("EncryptedRemoteObjects.getRemoteData <inner deferred>", [
|
||||
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this.reference()),
|
||||
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this),
|
||||
MochiKit.Base.method(this, 'setRemoteData'),
|
||||
], {trace:false});
|
||||
}
|
||||
@@ -542,3 +542,19 @@ Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(nul
|
||||
//-------------------------------------------------------------------------
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
||||
/*
|
||||
Clipperz.PM.DataModel.EncryptedRemoteObject.emptyObjectWithKey = function (aKey) {
|
||||
var key = "clipperz";
|
||||
var value = new Clipperz.ByteArray();
|
||||
var version = Clipperz.PM.Crypto.encryptingFunctions.currentVersion;
|
||||
// var remoteData = Clipperz.PM.Crypto.encrypt({'key':key, 'value':value, 'version':version});
|
||||
var remoteData = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(key, value);
|
||||
|
||||
return new Clipperz.PM.DataModel.EncryptedRemoteObject({
|
||||
'reference': Clipperz.PM.Crypto.randomKey(),
|
||||
'remoteData': remoteData,
|
||||
'retrieveKeyFunction': function () { return key; },
|
||||
});
|
||||
};
|
||||
*/
|
||||
@@ -41,11 +41,18 @@ Clipperz.PM.DataModel.Record = function(args) {
|
||||
this._retrieveDirectLoginIndexDataFunction = args.retrieveDirectLoginIndexDataFunction || null;
|
||||
this._setDirectLoginIndexDataFunction = args.setDirectLoginIndexDataFunction || null;
|
||||
this._removeDirectLoginIndexDataFunction = args.removeDirectLoginIndexDataFunction || null;
|
||||
|
||||
this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
|
||||
|
||||
this._retrieveAttachmentIndexDataFunction = args.retrieveAttachmentIndexDataFunction || null;
|
||||
this._setAttachmentIndexDataFunction = args.setAttachmentIndexDataFunction || null;
|
||||
this._removeAttachmentIndexDataFunction = args.removeAttachmentIndexDataFunction || null;
|
||||
this._createNewAttachmentFunction = args.createNewAttachmentFunction || null;
|
||||
|
||||
this._attachmentServerStatus = {};
|
||||
|
||||
this._tags = [];
|
||||
this._directLogins = {};
|
||||
this._attachments = {};
|
||||
this._versions = {};
|
||||
|
||||
this._currentRecordVersion = null;
|
||||
@@ -132,6 +139,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
setAttachmentServerStatus: function(anObject) {
|
||||
this._attachmentServerStatus = anObject;
|
||||
},
|
||||
|
||||
getAttachmentServerStatus: function() {
|
||||
return this._attachmentServerStatus;
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
/*
|
||||
'key': function () {
|
||||
@@ -283,6 +300,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
return MochiKit.Async.succeed(this._accessDate);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
attachmentsCount: function() {
|
||||
return MochiKit.Base.keys(this.attachments()).length;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'favicon': function () {
|
||||
@@ -374,6 +397,13 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
|
||||
deferredResult.addCallback(function () { return result; });
|
||||
|
||||
deferredResult.addMethod(this, 'attachments');
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
|
||||
deferredResult.addCallback(function () { return result; });
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
@@ -385,7 +415,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
return this._directLogins;
|
||||
},
|
||||
|
||||
'addDirectLogin': function (aDirectLogin) {
|
||||
'bindDirectLogin': function (aDirectLogin) {
|
||||
this._directLogins[aDirectLogin.reference()] = aDirectLogin;
|
||||
},
|
||||
|
||||
@@ -400,12 +430,13 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
'saveOriginalDirectLoginStatusToTransientState': function () {
|
||||
if (this.transientState().getValue('directLogins') == null) {
|
||||
// this.transientState().setValue('directLogins', this._directLogins)
|
||||
this.transientState().setValue('directLogins', {});
|
||||
MochiKit.Iter.forEach(MochiKit.Base.keys(this._directLogins), MochiKit.Base.bind(function(aKey) {
|
||||
this.transientState().setValue('directLogins' + '.' + aKey, this._directLogins[aKey])
|
||||
}, this))
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
'createNewDirectLogin': function () {
|
||||
this.saveOriginalDirectLoginStatusToTransientState();
|
||||
|
||||
@@ -453,6 +484,81 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
return result;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// TODO: !!!!
|
||||
'attachments': function () {
|
||||
return this._attachments;
|
||||
},
|
||||
|
||||
'bindAttachment': function (anAttachment) {
|
||||
this._attachments[anAttachment.reference()] = anAttachment;
|
||||
},
|
||||
|
||||
'attachmentWithReference': function (anAttachmentReference) {
|
||||
return this._attachments[anAttachmentReference];
|
||||
},
|
||||
|
||||
'createNewAttachmentFunction': function () {
|
||||
return this._createNewAttachmentFunction;
|
||||
},
|
||||
|
||||
'saveOriginalAttachmentStatusToTransientState': function () {
|
||||
if (this.transientState().getValue('attachments') == null) {
|
||||
// console.log("saving attachments to transient state: ts:", this.transientState());
|
||||
this.transientState().setValue('attachments', {});
|
||||
MochiKit.Iter.forEach(MochiKit.Base.keys(this._attachments), MochiKit.Base.bind(function(aKey) {
|
||||
// console.log("saving attachment to transient state:", this._attachments[aKey]);
|
||||
this.transientState().setValue('attachments' + '.' + aKey, this._attachments[aKey])
|
||||
}, this))
|
||||
}
|
||||
},
|
||||
|
||||
'createNewAttachment': function () {
|
||||
this.saveOriginalAttachmentStatusToTransientState();
|
||||
return this.createNewAttachmentFunction()(this);
|
||||
},
|
||||
|
||||
'removeAttachment': function(anAttachment) {
|
||||
this.saveOriginalAttachmentStatusToTransientState();
|
||||
return Clipperz.Async.callbacks("Record.removeAttachment", [
|
||||
MochiKit.Base.method(this, 'removeValue', 'attachments' + '.' + anAttachment.reference()),
|
||||
MochiKit.Base.bind(function () {
|
||||
this._removeAttachmentIndexDataFunction(anAttachment.reference());
|
||||
delete this._attachments[anAttachment.reference()]
|
||||
}, this)
|
||||
], {trace:false});
|
||||
|
||||
},
|
||||
|
||||
'attachmentReferences': function () {
|
||||
var result;
|
||||
|
||||
result = Clipperz.Async.callbacks("Record.attachmentReferences", [
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
function (someAttachments) {
|
||||
var result;
|
||||
var i,c;
|
||||
|
||||
result = [];
|
||||
c = someAttachments.length;
|
||||
for (i=0; i<c; i++) {
|
||||
result.push(Clipperz.Async.collectResults("Record.attachmentReferences - collectResults", {
|
||||
'_rowObject': MochiKit.Async.succeed,
|
||||
'_reference': MochiKit.Base.methodcaller('reference'),
|
||||
// 'label': MochiKit.Base.methodcaller('label'),
|
||||
// 'favicon': MochiKit.Base.methodcaller('favicon')
|
||||
}, {trace:false})(someAttachments[i]));
|
||||
};
|
||||
|
||||
return result;
|
||||
},
|
||||
Clipperz.Async.collectAll
|
||||
], {trace:false});
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'unpackRemoteData': function (someData) {
|
||||
@@ -508,6 +614,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
newVersionKey = Clipperz.PM.Crypto.randomKey();
|
||||
result = {};
|
||||
|
||||
result['attachments'] = MochiKit.Base.keys(this.attachments());
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Record.prepareRemoteDataWithKey", {trace:false});
|
||||
deferredResult.addCallbackList([
|
||||
Clipperz.Async.collectResults("Record.prepareRemoteDataWithKey - collect results", {
|
||||
@@ -581,7 +689,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
||||
result = someFilteredResults[0];
|
||||
break;
|
||||
default:
|
||||
Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
|
||||
//console.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
|
||||
result = someFilteredResults[0];
|
||||
break;
|
||||
}
|
||||
@@ -607,7 +715,7 @@ Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more
|
||||
var transientStateKey;
|
||||
|
||||
if (typeof(aVersionReference) == 'undefined') {
|
||||
Clipperz.log("ERROR; getVersionKey aVersionReference is undefined");
|
||||
console.log("ERROR; getVersionKey aVersionReference is undefined");
|
||||
}
|
||||
|
||||
transientStateKey = 'versionKeys' + '.' + aVersionReference;
|
||||
@@ -757,60 +865,6 @@ Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
/*
|
||||
'hasPendingChanges': function () {
|
||||
var deferredResult;
|
||||
|
||||
if (this.hasInitiatedObjectDataStore()) {
|
||||
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChanges", {trace:false});
|
||||
deferredResult.collectResults({
|
||||
'super': MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasPendingChanges, this),
|
||||
'currentVersion': [
|
||||
// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
|
||||
// MochiKit.Base.methodcaller('hasPendingChanges')
|
||||
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
|
||||
],
|
||||
'directLogins': [
|
||||
MochiKit.Base.method(this, 'directLogins'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
|
||||
Clipperz.Async.collectAll,
|
||||
Clipperz.Async.or
|
||||
// function(someValues) {
|
||||
// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
|
||||
// }
|
||||
]
|
||||
});
|
||||
deferredResult.addCallback(function (aValue) { console.log("Record.hasPendingChanges", aValue); return aValue; });
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function(someValues) {
|
||||
var result;
|
||||
result = MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
|
||||
|
||||
if ((result == false) && (this.isBrandNew() == false)) {
|
||||
result = MochiKit.Iter.some(MochiKit.Base.values(this.transientState().getValue('hasPendingChanges.indexData')), MochiKit.Base.operator.identity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, this));
|
||||
|
||||
deferredResult.callback();
|
||||
} else {
|
||||
deferredResult = Clipperz.Async.callbacks("Recrod.hasPendingChanges [hasInitiatedObjectDataStore == false]", [
|
||||
MochiKit.Base.method(this, 'directLogins'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
|
||||
Clipperz.Async.collectAll,
|
||||
Clipperz.Async.or
|
||||
// function(someValues) {
|
||||
// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
|
||||
// }
|
||||
], {trace:false})
|
||||
}
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
*/
|
||||
|
||||
'hasPendingChanges': function () {
|
||||
var deferredResult;
|
||||
@@ -836,27 +890,18 @@ deferredResult.addCallback(function (aValue) { console.log("Record.hasPendingCha
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
|
||||
Clipperz.Async.collectAll,
|
||||
Clipperz.Async.or
|
||||
],
|
||||
'attachments': [
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
|
||||
Clipperz.Async.collectAll,
|
||||
Clipperz.Async.or
|
||||
]
|
||||
});
|
||||
//deferredResult.addCallback(function (someValues) {
|
||||
// if (recordReference == 'd620764a656bfd4e1d3758500d5db72e460a0cf729d56ed1a7755b5725c50045') {
|
||||
// console.log("Record.hasPendingChanges VALUES", someValues);
|
||||
// }
|
||||
// return someValues;
|
||||
//})
|
||||
}, {trace:true});
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function(someValues) {
|
||||
var result;
|
||||
result = MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
|
||||
/*
|
||||
if ((result == false) && (this.isBrandNew() == false)) {
|
||||
console.log("TRANSIENT STATE", this.transientState());
|
||||
console.log("TRANSIENT STATE - hasPendingChanges", this.transientState().getValue('hasPendingChanges.indexData'));
|
||||
result = MochiKit.Iter.some(MochiKit.Base.values(this.transientState().getValue('hasPendingChanges.indexData')), MochiKit.Base.operator.identity);
|
||||
}
|
||||
console.log("Record.hasPendingChanges RESULT", result);
|
||||
*/
|
||||
return result;
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function (someValues) {
|
||||
return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
|
||||
}, this));
|
||||
|
||||
deferredResult.callback();
|
||||
@@ -911,6 +956,8 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
//=========================================================================
|
||||
|
||||
'revertChanges': function () {
|
||||
// console.log("Revert changes: attachments in transient state", this.transientState().getValue('attachments'));
|
||||
|
||||
var deferredResult;
|
||||
// var recordReference = this.reference();
|
||||
|
||||
@@ -926,19 +973,19 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
// return true;
|
||||
//});
|
||||
deferredResult.addIf([
|
||||
//function (aValue) { console.log("Record.revertChanges - 1"); return aValue; },
|
||||
MochiKit.Base.method(this, 'cancelRevertedAttachmentsUpload'),
|
||||
|
||||
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
|
||||
//function (aValue) { console.log("Record.revertChanges - 2"); return aValue; },
|
||||
|
||||
MochiKit.Base.method(this, 'directLogins'),
|
||||
//function (aValue) { console.log("Record.revertChanges - 3"); return aValue; },
|
||||
MochiKit.Base.values,
|
||||
//function (aValue) { console.log("Record.revertChanges - 4"); return aValue; },
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
|
||||
//function (aValue) { console.log("Record.revertChanges - 5"); return aValue; },
|
||||
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
|
||||
|
||||
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this),
|
||||
//function (aValue) { console.log("Record.revertChanges - 6"); return aValue; },
|
||||
MochiKit.Base.method(this, '_getObjectDataStore'),
|
||||
], [
|
||||
MochiKit.Async.succeed
|
||||
@@ -962,6 +1009,19 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
cancelRevertedAttachmentsUpload: function() {
|
||||
var reference;
|
||||
|
||||
var transientStateAttachments = (this.transientState() ? this.transientState().values()['attachments'] : {}) || {};
|
||||
var savedReferences = MochiKit.Base.keys(transientStateAttachments);
|
||||
|
||||
for (reference in this.attachments()) {
|
||||
if (savedReferences.indexOf(reference) < 0) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', this.attachments()[reference]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'resetTransientState': function (isCommitting) {
|
||||
@@ -969,6 +1029,8 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
// this._directLogins = this.transientState().getValue('directLogins');
|
||||
// }
|
||||
|
||||
// console.log("Reset transient state: attachments:", this.transientState().getValue('directLogins'));
|
||||
|
||||
return Clipperz.Async.callbacks("Record.resetTransientState", [
|
||||
//- MochiKit.Base.method(this, 'getCurrentRecordVersion'),
|
||||
//- MochiKit.Base.methodcaller('resetTransientState'),
|
||||
@@ -979,9 +1041,18 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
|
||||
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
|
||||
|
||||
MochiKit.Base.bind(function () {
|
||||
if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
|
||||
this._directLogins = this.transientState().getValue('directLogins');
|
||||
if (isCommitting == false) {
|
||||
if (this.transientState().getValue('directLogins') != null) {
|
||||
this._directLogins = this.transientState().getValue('directLogins');
|
||||
}
|
||||
if (this.transientState().getValue('attachments') != null) {
|
||||
this._attachments = this.transientState().getValue('attachments');
|
||||
}
|
||||
}
|
||||
}, this),
|
||||
|
||||
@@ -1003,6 +1074,10 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'commitTransientState'),
|
||||
MochiKit.Base.method(this, 'directLogins'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState')),
|
||||
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState'))
|
||||
], [
|
||||
MochiKit.Async.succeed
|
||||
@@ -1028,6 +1103,20 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'retrieveAttachmentIndexDataFunction': function () {
|
||||
return this._retrieveAttachmentIndexDataFunction;
|
||||
},
|
||||
|
||||
'setAttachmentIndexDataFunction': function () {
|
||||
return this._setAttachmentIndexDataFunction;
|
||||
},
|
||||
|
||||
'removeAttachmentIndexDataFunction': function () {
|
||||
return this._removeAttachmentIndexDataFunction;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'deleteAllCleanTextData': function () {
|
||||
// return Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData.apply(this, arguments);
|
||||
|
||||
@@ -1040,6 +1129,10 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
|
||||
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
|
||||
|
||||
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData, this)
|
||||
], {trace:false});
|
||||
},
|
||||
@@ -1061,6 +1154,12 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
|
||||
Clipperz.Async.collectAll
|
||||
],
|
||||
'attachments': [
|
||||
MochiKit.Base.method(this, 'attachments'),
|
||||
MochiKit.Base.values,
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
|
||||
Clipperz.Async.collectAll
|
||||
],
|
||||
'super': [
|
||||
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData, this)
|
||||
]
|
||||
@@ -1131,10 +1230,12 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
MochiKit.Base.method(aRecord, 'directLogins'), MochiKit.Base.values,
|
||||
//function (aValue) { console.log("-> SETUP WITH RECORD: DirectLogin Values", aValue); return aValue; },
|
||||
// TODO: possibly broken implementation of direct login cloning
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addDirectLogin')),
|
||||
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'bindDirectLogin')),
|
||||
//function (aValue) { console.log("-> DirectLogin Values", aValue); return aValue; },
|
||||
// Clipperz.Async.collectAll,
|
||||
|
||||
// TODO: attachments
|
||||
|
||||
MochiKit.Base.bind(function () { return this; }, this)
|
||||
], {trace:false});
|
||||
},
|
||||
@@ -1230,7 +1331,8 @@ Clipperz.PM.DataModel.Record.defaultCardInfo = {
|
||||
'_isArchived': MochiKit.Base.methodcaller('isArchived'),
|
||||
'_isBrandNew': MochiKit.Base.methodcaller('isBrandNew'),
|
||||
'label': MochiKit.Base.methodcaller('label'),
|
||||
'favicon': MochiKit.Base.methodcaller('favicon')
|
||||
'favicon': MochiKit.Base.methodcaller('favicon'),
|
||||
'attachmentsCount': MochiKit.Base.methodcaller('attachmentsCount'),
|
||||
};
|
||||
Clipperz.PM.DataModel.Record.defaultSearchField = '_searchableContent';
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ refer to http://www.clipperz.com.
|
||||
|
||||
try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
|
||||
throw "Clipperz.PM.DataModel.User.Header.RecordIndex depends on Clipperz.PM.DataModel.User!";
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
|
||||
|
||||
@@ -49,13 +49,25 @@ Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
|
||||
}
|
||||
});
|
||||
|
||||
//console.log("RECORD INDEX args", args);
|
||||
this._attachmentsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
|
||||
'name': 'attachmentsData',
|
||||
'retrieveKeyFunction': args.retrieveKeyFunction,
|
||||
'remoteData': {
|
||||
'data': args.attachmentsData['data'],
|
||||
'version': args.encryptedDataVersion,
|
||||
}
|
||||
});
|
||||
|
||||
this._tagsData =
|
||||
this._lock = new MochiKit.Async.DeferredLock();
|
||||
this._transientState = null;
|
||||
|
||||
this._retrieveRecordDetailFunction = args.retrieveRecordDetailFunction || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
|
||||
this._recordsIndex = args.recordsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
this._directLoginsIndex = args.directLoginsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
this._attachmentsIndex = args.attachmentsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
|
||||
this._records = null;
|
||||
|
||||
@@ -97,6 +109,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'attachmentsIndex': function () {
|
||||
return this._attachmentsIndex;
|
||||
},
|
||||
|
||||
'attachmentsData': function () {
|
||||
return this._attachmentsData;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'lock': function () {
|
||||
return this._lock;
|
||||
},
|
||||
@@ -164,6 +186,28 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'getAttachmentIndexData': function (anAttachmentReference) {
|
||||
// TODO:
|
||||
return this.attachmentsData().getValue(this.attachmentsIndex()[anAttachmentReference]);
|
||||
},
|
||||
|
||||
'setAttachmentIndexData': function (anAttachmentReference, aKey, aValue) {
|
||||
// TODO:
|
||||
return this.attachmentsData().setValue(this.attachmentsIndex()[anAttachmentReference] + '.' + aKey, aValue);
|
||||
},
|
||||
|
||||
'addAttachmentIndexData': function (anAttachmentReference) {
|
||||
// TODO:
|
||||
return this.attachmentsData().setValue(this.attachmentsIndex()[anAttachmentReference], {});
|
||||
},
|
||||
|
||||
'removeAttachmentIndexData': function (anAttachmentReference) {
|
||||
// TODO:
|
||||
return this.attachmentsData().removeValue(this.attachmentsIndex()[anAttachmentReference])
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'records': function () {
|
||||
var deferredResult;
|
||||
|
||||
@@ -184,6 +228,9 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
],
|
||||
'directLogins': [
|
||||
MochiKit.Base.method(this.directLoginsData(), 'values')
|
||||
],
|
||||
'attachments': [
|
||||
MochiKit.Base.method(this.attachmentsData(), 'values')
|
||||
]
|
||||
})
|
||||
innerDeferredResult.addCallback(MochiKit.Base.bind(function (someData) {
|
||||
@@ -193,6 +240,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
|
||||
recordsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.recordsIndex());
|
||||
directLoginsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.directLoginsIndex());
|
||||
attachmentsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.attachmentsIndex());
|
||||
|
||||
this._records = {};
|
||||
|
||||
@@ -201,13 +249,15 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
var reference;
|
||||
var updateDate;
|
||||
var accessDate;
|
||||
var attachmentsCount;
|
||||
|
||||
reference = recordsInvertedIndex[indexReference];
|
||||
|
||||
if (typeof(someData['recordsStats'][reference]) != 'undefined') {
|
||||
updateDate = someData['recordsStats'][reference]['updateDate'];
|
||||
accessDate = someData['recordsStats'][reference]['accessDate'];
|
||||
|
||||
// attachmentsCount = (someData['attachmentsCount'][reference]) ? someData['attachmentsCount'][reference] : 0;
|
||||
|
||||
record = new Clipperz.PM.DataModel.Record({
|
||||
'reference': reference,
|
||||
'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
|
||||
@@ -221,8 +271,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
|
||||
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
|
||||
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
|
||||
'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
|
||||
|
||||
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
|
||||
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin'),
|
||||
|
||||
'retrieveAttachmentIndexDataFunction': MochiKit.Base.method(this, 'getAttachmentIndexData'),
|
||||
'setAttachmentIndexDataFunction': MochiKit.Base.method(this, 'setAttachmentIndexData'),
|
||||
'removeAttachmentIndexDataFunction': MochiKit.Base.method(this, 'removeAttachmentIndexData'),
|
||||
'createNewAttachmentFunction': MochiKit.Base.method(this, 'createNewAttachment'),
|
||||
});
|
||||
|
||||
this._records[reference] = record;
|
||||
@@ -248,6 +302,24 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("RecordIndex.Records: attachments data:", someData['attachments']);
|
||||
for (indexReference in someData['attachments']) {
|
||||
var reference;
|
||||
var record;
|
||||
|
||||
reference = attachmentsInvertedIndex[indexReference];
|
||||
record = this._records[recordsInvertedIndex[someData['attachments'][indexReference]['record']]];
|
||||
|
||||
if (record != null) {
|
||||
new Clipperz.PM.DataModel.Attachment({
|
||||
'reference': reference,
|
||||
'record': record
|
||||
});
|
||||
} else {
|
||||
Clipperz.logWarning("WARNING: Attachment without a matching RECORD!!");
|
||||
}
|
||||
}
|
||||
|
||||
return this._records;
|
||||
}, this));
|
||||
innerDeferredResult.callback();
|
||||
@@ -295,8 +367,12 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
|
||||
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
|
||||
'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
|
||||
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin'),
|
||||
|
||||
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
|
||||
'retrieveAttachmentIndexDataFunction': MochiKit.Base.method(this, 'getAttachmentIndexData'),
|
||||
'setAttachmentIndexDataFunction': MochiKit.Base.method(this, 'setAttachmentIndexData'),
|
||||
'removeAttachmentIndexDataFunction': MochiKit.Base.method(this, 'removeAttachmentIndexData'),
|
||||
'createNewAttachmentFunction': MochiKit.Base.method(this, 'createNewAttachment'),
|
||||
});
|
||||
|
||||
this.transientState().setValue('newRecordsReferences' + '.' + newRecord.reference(), newRecord);
|
||||
@@ -372,6 +448,40 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'removeAttachment': function (anAttachment) {
|
||||
// TODO:
|
||||
// this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLogin.reference()]);
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'createNewAttachment': function (aRecord) {
|
||||
// TODO:
|
||||
|
||||
// var newDirectLogin;
|
||||
var newAttachment;
|
||||
// var newDirectLoginIndexValue;
|
||||
var newAttachmentIndexValue;
|
||||
|
||||
// newDirectLogin = new Clipperz.PM.DataModel.DirectLogin({record:aRecord});
|
||||
newAttachment = new Clipperz.PM.DataModel.Attachment({'record':aRecord});
|
||||
// newDirectLoginIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.directLoginsIndex()))) + 1;
|
||||
newAttachmentIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.attachmentsIndex()))) + 1;
|
||||
|
||||
// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
|
||||
this.transientState().setValue('newAttachmentReferences' + '.' + newAttachment.reference(), newAttachment);
|
||||
|
||||
// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
|
||||
this.attachmentsIndex()[newAttachment.reference()] = newAttachmentIndexValue;
|
||||
// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
|
||||
this.attachmentsData().setValue(this.attachmentsIndex()[newAttachment.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
|
||||
|
||||
// return newDirectLogin;
|
||||
return newAttachment;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'deleteAllCleanTextData': function () {
|
||||
return Clipperz.Async.callbacks("User.Header.RecordIndex.deleteAllCleanTextData", [
|
||||
MochiKit.Base.method(this, 'recordsData'),
|
||||
@@ -396,6 +506,10 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
MochiKit.Base.method(this, 'directLoginsData'),
|
||||
MochiKit.Base.methodcaller('hasAnyCleanTextData')
|
||||
],
|
||||
'attachmentsData': [
|
||||
MochiKit.Base.method(this, 'attachmentsData'),
|
||||
MochiKit.Base.methodcaller('hasAnyCleanTextData')
|
||||
],
|
||||
});
|
||||
|
||||
deferredResult.addCallback(Clipperz.Async.or);
|
||||
@@ -418,8 +532,13 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
'directLoginsData': [
|
||||
MochiKit.Base.method(this, 'directLoginsData'),
|
||||
MochiKit.Base.methodcaller('hasPendingChanges')
|
||||
],
|
||||
'attachments': [
|
||||
MochiKit.Base.method(this, 'attachmentsData'),
|
||||
MochiKit.Base.methodcaller('hasPendingChanges')
|
||||
]
|
||||
});
|
||||
//deferredResult.addCallback(function (aValue) { console.log("HAS PENDING CHANGES", aValue); return aValue; });
|
||||
deferredResult.addCallback(Clipperz.Async.or);
|
||||
deferredResult.callback();
|
||||
|
||||
@@ -437,6 +556,9 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
|
||||
MochiKit.Base.method(this, 'directLoginsData'),
|
||||
MochiKit.Base.methodcaller('commitTransientState'),
|
||||
|
||||
MochiKit.Base.method(this, 'attachmentsData'),
|
||||
MochiKit.Base.methodcaller('commitTransientState'),
|
||||
|
||||
MochiKit.Base.method(this, 'resetTransientState', true)
|
||||
], {trace:false});
|
||||
@@ -474,9 +596,21 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
}
|
||||
}, this),
|
||||
|
||||
MochiKit.Base.bind(function () {
|
||||
// TODO:
|
||||
var attachmentReference;
|
||||
|
||||
for (attachmentReference in this.transientState().getValue('newAttachmentReferences')) {
|
||||
delete this.attachmentsIndex()[attachmentReference];
|
||||
}
|
||||
}, this),
|
||||
|
||||
MochiKit.Base.method(this, 'directLoginsData'),
|
||||
MochiKit.Base.methodcaller('revertChanges'),
|
||||
|
||||
MochiKit.Base.method(this, 'attachmentsData'),
|
||||
MochiKit.Base.methodcaller('revertChanges'),
|
||||
|
||||
MochiKit.Base.method(this, 'resetTransientState', false)
|
||||
], {trace:false});
|
||||
},
|
||||
@@ -528,6 +662,15 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
|
||||
]
|
||||
});
|
||||
deferredResult.addCallback(Clipperz.Async.setItem, result, 'directLogins');
|
||||
|
||||
deferredResult.collectResults({
|
||||
'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.attachmentsIndex()),
|
||||
'data': [
|
||||
MochiKit.Base.method(this.attachmentsData(), 'prepareRemoteDataWithKey', aKey),
|
||||
MochiKit.Base.itemgetter('data')
|
||||
]
|
||||
});
|
||||
deferredResult.addCallback(Clipperz.Async.setItem, result, 'attachments');
|
||||
|
||||
deferredResult.addCallback(MochiKit.Async.succeed, result);
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
|
||||
'recordsData': {'data':null, 'index':{}},
|
||||
'recordsStats': null,
|
||||
'directLoginsData': {'data':null, 'index':{}},
|
||||
'attachmentsData': {'data':null, 'index':{}},
|
||||
'encryptedDataVersion': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
|
||||
'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
|
||||
}),
|
||||
@@ -465,13 +466,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
|
||||
case '0.1':
|
||||
var headerData;
|
||||
|
||||
//console.log("Server data", someServerData);
|
||||
headerData = Clipperz.Base.evalJSON(someServerData['header']);
|
||||
|
||||
//console.log("headerData", headerData);
|
||||
recordsIndex = new Clipperz.PM.DataModel.User.Header.RecordIndex({
|
||||
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
|
||||
'recordsData': headerData['records'],
|
||||
'recordsStats': someServerData['recordsStats'],
|
||||
'directLoginsData': headerData['directLogins'],
|
||||
'attachmentsData': headerData['attachments'] || {'data': null, 'index':{}},
|
||||
'encryptedDataVersion': someServerData['version'],
|
||||
'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
|
||||
});
|
||||
@@ -695,7 +699,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
|
||||
result = someFilteredResults[0];
|
||||
break;
|
||||
default:
|
||||
Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
|
||||
//console.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
|
||||
result = someFilteredResults[0];
|
||||
break;
|
||||
}
|
||||
@@ -723,8 +727,13 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'getRecordDetail': function (aRecordReference) {
|
||||
return this.connection().message('getRecordDetail', {reference: aRecordReference});
|
||||
'getRecordDetail': function (aRecord) {
|
||||
// return this.connection().message('getRecordDetail', {reference: aRecordReference});
|
||||
|
||||
return Clipperz.Async.callbacks("User.getRecordDetail", [
|
||||
MochiKit.Base.method(this.connection(), 'message', 'getRecordDetail', {reference: aRecord.reference()}),
|
||||
function(someInfo) { aRecord.setAttachmentServerStatus(someInfo['attachmentStatus']); return someInfo;}, // Couldn't find a better way...
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@@ -750,6 +759,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
|
||||
'createNewRecordFromJSON': function(someJSON) {
|
||||
var deferredResult;
|
||||
|
||||
// TODO: how do we handle attachments?
|
||||
deferredResult = new Clipperz.Async.Deferred("User.createNewRecordFromJSON", {trace:false});
|
||||
deferredResult.collectResults({
|
||||
'recordIndex': MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
|
||||
@@ -770,6 +780,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
|
||||
});
|
||||
|
||||
return Clipperz.Async.callbacks("User.createNewRecordFromJSON__inner", [
|
||||
// TODO: check if we should invoke the create new direct login methon on Record instead of calling it directly on the index
|
||||
MochiKit.Base.method(recordIndex, 'createNewDirectLogin', record),
|
||||
MochiKit.Base.methodcaller('setLabel', aDirectLogin['label']),
|
||||
MochiKit.Base.methodcaller('setBookmarkletConfiguration', configuration),
|
||||
@@ -937,6 +948,22 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/*
|
||||
'addNewAttachment': function(anAttachment) {
|
||||
console.log("Adding attachment", anAttachment);
|
||||
},
|
||||
*/
|
||||
'uploadAttachment': function(aRecordReference, anAttachmentReference, someData) {
|
||||
// TODO: pass a callback to handle onProgress events (and modify Connection accordingly)
|
||||
this.connection().message('uploadAttachment', {
|
||||
'recordReference': aRecordReference,
|
||||
'attachmentReference': anAttachmentReference,
|
||||
'data': someData
|
||||
});
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'hasPendingChanges': function () {
|
||||
@@ -1035,6 +1062,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
|
||||
header = {};
|
||||
header['records'] = someHeaderPackedData['recordIndex']['records'];
|
||||
header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
|
||||
header['attachments'] = someHeaderPackedData['recordIndex']['attachments'];
|
||||
header['preferences'] = {'data': someHeaderPackedData['preferences']['data']};
|
||||
header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']};
|
||||
header['version'] = '0.1';
|
||||
|
||||
@@ -95,6 +95,27 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
'justPayToll': function(aRequestType) {
|
||||
var deferredResult;
|
||||
|
||||
if (this.shouldPayTolls()) {
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.justPayToll", {trace:false});
|
||||
|
||||
if (this.tolls()[aRequestType].length == 0) {
|
||||
deferredResult.addMethod(this, 'sendMessage', 'knock', {requestType:aRequestType});
|
||||
deferredResult.addMethod(this, 'setTollCallback');
|
||||
}
|
||||
deferredResult.addMethod(this.tolls()[aRequestType], 'pop');
|
||||
deferredResult.addCallback(MochiKit.Base.methodcaller('deferredPay'));
|
||||
|
||||
deferredResult.callback();
|
||||
} else {
|
||||
deferredResult = MochiKit.Async.succeed(null);
|
||||
}
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'addToll': function(aToll) {
|
||||
@@ -108,7 +129,7 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
|
||||
this.addToll(new Clipperz.PM.Toll(someParameters['toll']));
|
||||
}
|
||||
|
||||
return someParameters['result'];
|
||||
return someParameters['result']; // <- This is what will be returned by the message call
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
@@ -121,8 +142,8 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
|
||||
return this.processMessage('handshake', someParameters, 'CONNECT');
|
||||
},
|
||||
|
||||
'message': function (someParameters) {
|
||||
return this.processMessage('message', someParameters, 'MESSAGE');
|
||||
'message': function (someParameters, someOptionalParameters) {
|
||||
return this.processMessage('message', someParameters, 'MESSAGE', someOptionalParameters);
|
||||
},
|
||||
|
||||
'logout': function (someParameters) {
|
||||
@@ -145,33 +166,65 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'processMessage': function (aFunctionName, someParameters, aRequestType) {
|
||||
'processMessage': function (aFunctionName, someParameters, aRequestType, someOptionalParameters) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.processMessage", {trace:false});
|
||||
deferredResult.addMethod(this, 'payToll', aRequestType);
|
||||
deferredResult.addMethod(this, 'sendMessage', aFunctionName);
|
||||
// deferredResult.addMethod(this, 'sendMessage', aFunctionName);
|
||||
deferredResult.addCallback(MochiKit.Base.bind(function(aResult){
|
||||
return this.sendMessage(aFunctionName, aResult, someOptionalParameters);
|
||||
}, this));
|
||||
deferredResult.addMethod(this, 'setTollCallback');
|
||||
deferredResult.callback(someParameters);
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
'processAttachmentMessage': function(aMessageCallback) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.uploadAttachment", {trace:false});
|
||||
deferredResult.addMethod(this, 'justPayToll', 'MESSAGE');
|
||||
deferredResult.addCallback(aMessageCallback);
|
||||
deferredResult.addErrback(MochiKit.Base.method(this, 'handleError'));
|
||||
deferredResult.addMethod(this, 'setTollCallback');
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
'uploadAttachment': function(someArguments, aProgressCallback, aSharedSecret) {
|
||||
var messageCallback = MochiKit.Base.method(this, '_uploadAttachment', someArguments, aProgressCallback, aSharedSecret);
|
||||
|
||||
return this.processAttachmentMessage(messageCallback);
|
||||
},
|
||||
|
||||
'downloadAttachment': function(someArguments, aProgressCallback, aSharedSecret) {
|
||||
var messageCallback = MochiKit.Base.method(this, '_downloadAttachment', someArguments, aProgressCallback, aSharedSecret);
|
||||
|
||||
return this.processAttachmentMessage(messageCallback);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'_sendMessage': function (aFunctionName, aVersion, someParameters) {
|
||||
'_uploadAttachment': function (someArguments, aProgressCallback, aSharedSecret) {
|
||||
throw Clipperz.Base.exception.AbstractMethod;
|
||||
},
|
||||
|
||||
'sendMessage': function (aFunctionName, someParameters) {
|
||||
'_sendMessage': function (aFunctionName, aVersion, someParameters, someOptionalParameters) {
|
||||
throw Clipperz.Base.exception.AbstractMethod;
|
||||
},
|
||||
|
||||
'sendMessage': function (aFunctionName, someParameters, someOptionalParameters) {
|
||||
var deferredResult;
|
||||
|
||||
//console.log("PROXY.sendMessage", aFunctionName, someParameters);
|
||||
// TODO: read actual application version for a property set at build time
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.sendMessage", {trace:false});
|
||||
deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version');
|
||||
deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version', someParameters, someOptionalParameters);
|
||||
deferredResult.addErrback(MochiKit.Base.method(this, 'handleError'));
|
||||
deferredResult.callback(someParameters);
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'_sendMessage': function(aFunctionName, aVersion, someParameters) {
|
||||
'_sendMessage': function(aFunctionName, aVersion, someParameters, someOptionalParameters) {
|
||||
var deferredResult;
|
||||
var parameters;
|
||||
|
||||
@@ -71,13 +71,19 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
|
||||
version: aVersion,
|
||||
parameters: Clipperz.Base.serializeJSON(someParameters)
|
||||
};
|
||||
|
||||
someOptionalParameters = someOptionalParameters || {};
|
||||
|
||||
//console.log("PROXY.JSON._sendMessage", parameters);
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._sendMessage", {trace:false});
|
||||
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent');
|
||||
deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), {
|
||||
deferredResult.addCallback(Clipperz.Async.doXHR, this.url(), {
|
||||
// deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), {
|
||||
method:'POST',
|
||||
sendContent:MochiKit.Base.queryString(parameters),
|
||||
headers:{"Content-Type":"application/x-www-form-urlencoded"}
|
||||
headers:{"Content-Type":"application/x-www-form-urlencoded"},
|
||||
// downloadProgress:someOptionalParameters['downloadProgressCallback'] || null,
|
||||
// uploadProgress:someOptionalParameters['uploadProgressCallback'] || null,
|
||||
});
|
||||
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestReceived');
|
||||
deferredResult.addCallback(MochiKit.Base.itemgetter('responseText'));
|
||||
@@ -94,6 +100,114 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// TODO: test
|
||||
'_uploadAttachment': function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
var formData;
|
||||
var deferredResult;
|
||||
|
||||
var parameters = {
|
||||
'toll': aToll,
|
||||
'parameters': {
|
||||
'message': 'uploadAttachment',
|
||||
'srpSharedSecret': aSharedSecret,
|
||||
'parameters': {
|
||||
'attachmentReference': someArguments['attachmentReference'],
|
||||
'recordReference': someArguments['recordReference'],
|
||||
'version': someArguments['version']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var blob = new Blob([someArguments['arrayBufferData']]);
|
||||
|
||||
formData = new FormData();
|
||||
formData.append('method', 'message');
|
||||
formData.append('version', 'fake-app-version'); // Not implemented anywhere yet
|
||||
formData.append('parameters', JSON.stringify(parameters));
|
||||
formData.append('data', blob);
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._sendMessage", {trace:false});
|
||||
//deferredResult.addCallback(function(){console.log("About to send request");});
|
||||
deferredResult.addCallback(Clipperz.Async.doXHR, this.url(), {
|
||||
method:'POST',
|
||||
sendContent:formData,
|
||||
// headers:{"Content-Type":"application/x-www-form-urlencoded"},
|
||||
uploadProgress: aProgressCallback || null,
|
||||
});
|
||||
//deferredResult.addCallback(function(something){console.log("Done sending request"); return something;});
|
||||
deferredResult.addCallback(function (someValues) {
|
||||
if (someValues['result'] == 'EXCEPTION') {
|
||||
throw someValues['message'];
|
||||
}
|
||||
|
||||
// TODO: check return value with actual request. Expected: ArrayBuffer
|
||||
return someValues;
|
||||
});
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
// TODO: test
|
||||
'_downloadAttachment': function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
var deferredResult;
|
||||
var parameters;
|
||||
|
||||
var innerParameters = {
|
||||
'toll': aToll,
|
||||
'parameters': {
|
||||
'message': 'downloadAttachment',
|
||||
'srpSharedSecret': aSharedSecret,
|
||||
'parameters': {
|
||||
'reference': someArguments['reference']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameters = {
|
||||
method: 'message',
|
||||
version: 'fake-app-version',
|
||||
parameters: Clipperz.Base.serializeJSON(innerParameters)
|
||||
};
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._downloadAttachment", {trace:false});
|
||||
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent');
|
||||
//deferredResult.addCallback(function(){console.log("About to send request");});
|
||||
deferredResult.addCallback(Clipperz.Async.doXHR, this.url(), {
|
||||
method:'POST',
|
||||
responseType:'arraybuffer',
|
||||
sendContent:MochiKit.Base.queryString(parameters),
|
||||
headers:{"Content-Type":"application/x-www-form-urlencoded"},
|
||||
downloadProgress:aProgressCallback || null,
|
||||
});
|
||||
deferredResult.addCallback(
|
||||
function(something){
|
||||
// console.log("Done sending request", something);
|
||||
return something;
|
||||
});
|
||||
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestReceived');
|
||||
deferredResult.addCallback(MochiKit.Base.itemgetter('response'));
|
||||
deferredResult.addCallback(function (anArrayBuffer) {
|
||||
var result = (anArrayBuffer.byteLength > 0) ? anArrayBuffer : false;
|
||||
|
||||
var DEBUG = 'PUT_A_BREAKPOINT_HERE';
|
||||
|
||||
return {
|
||||
result: anArrayBuffer,
|
||||
};
|
||||
})
|
||||
// deferredResult.addErrback(function(something) {
|
||||
// return something;
|
||||
// });
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) {
|
||||
this._tolls = {};
|
||||
this._currentStaticConnection = null;
|
||||
|
||||
this.NETWORK_SIMULATED_SPEED = 100*1024;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -233,9 +235,9 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'processMessage': function (aFunctionName, someParameters) {
|
||||
'processMessage': function (aFunctionName, someParameters, someOptionalParameters) {
|
||||
var deferredResult;
|
||||
|
||||
|
||||
try {
|
||||
var result;
|
||||
var connection;
|
||||
@@ -266,7 +268,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
|
||||
this.storeConnectionForRequestWithConnectionAndResponse(aFunctionName, someParameters, connection, result);
|
||||
|
||||
deferredResult = MochiKit.Async.succeed(result);
|
||||
deferredResult = MochiKit.Async.succeed(result)
|
||||
} catch (exception) {
|
||||
deferredResult = MochiKit.Async.fail(exception);
|
||||
}
|
||||
@@ -274,6 +276,69 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
uploadAttachment: function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
var connection = this.currentStaticConnection();
|
||||
var attachmentReference = someArguments['attachmentReference'];
|
||||
|
||||
if (this.isReadOnly() == false) {
|
||||
connection['userData']['attachments'][attachmentReference] = {
|
||||
'record': someArguments['recordReference'],
|
||||
'status': 'AVAILABLE',
|
||||
'data': someArguments['arrayBufferData'],
|
||||
'version': someArguments['version'],
|
||||
};
|
||||
|
||||
return this.simulateNetworkDelayResponse(someArguments['arrayBufferData'].length, aProgressCallback, {
|
||||
result: {},
|
||||
toll: this.getTollForRequestType('MESSAGE')
|
||||
});
|
||||
} else {
|
||||
throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
|
||||
}
|
||||
},
|
||||
|
||||
downloadAttachment: function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
var connection = this.currentStaticConnection();
|
||||
var reference = someArguments['reference'];
|
||||
var result = connection['userData']['attachments'][reference]['data'];
|
||||
|
||||
return this.simulateNetworkDelayResponse(result.length, aProgressCallback, {
|
||||
result: result,
|
||||
// toll: this.getTollForRequestType('MESSAGE')
|
||||
});
|
||||
},
|
||||
|
||||
simulateNetworkDelayResponse: function(payloadSize, progressCallback, aResponse) {
|
||||
var deferredResult;
|
||||
var i;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Proxy.Offline.DataStore.simulateNetworkDelay", {trace:false});
|
||||
|
||||
for (i = 0; i < payloadSize/this.NETWORK_SIMULATED_SPEED; i++) {
|
||||
var loaded = i*this.NETWORK_SIMULATED_SPEED;
|
||||
deferredResult.addCallback(MochiKit.Async.wait, 1);
|
||||
deferredResult.addMethod(this, 'runProgressCallback', progressCallback, loaded, payloadSize);
|
||||
}
|
||||
|
||||
deferredResult.addCallback(MochiKit.Async.succeed, aResponse);
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
runProgressCallback: function(aCallback, aLoadedValue, aTotalValue) {
|
||||
var fakeProgressEvent = {
|
||||
'lengthComputable': true,
|
||||
'loaded': aLoadedValue,
|
||||
'total': aTotalValue,
|
||||
}
|
||||
|
||||
return aCallback(fakeProgressEvent);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'_knock': function(aConnection, someParameters) {
|
||||
@@ -340,7 +405,9 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
userData = this.data()['users'][someParameters.parameters.C];
|
||||
otpsData = this.data()['onetimePasswords'];
|
||||
|
||||
//console.log("Proxy.Offline.DataStore._handshake: otpsData:", otpsData);
|
||||
if (! userData['attachments']) {
|
||||
userData['attachments'] = {};
|
||||
}
|
||||
|
||||
userOTPs = {};
|
||||
MochiKit.Base.map(function(aOTP) {
|
||||
@@ -349,9 +416,6 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
}
|
||||
},MochiKit.Base.values(otpsData));
|
||||
|
||||
//console.log("Proxy.Offline.DataStore._handshake: userOTPs:", userOTPs);
|
||||
//console.log("Proxy.Offline.DataStore._handshake(): userOTPs:",userOTPs);
|
||||
|
||||
if ((typeof(userData) != 'undefined') && (userData['version'] == someParameters.version)) {
|
||||
aConnection['userData'] = userData;
|
||||
aConnection['userOTPs'] = userOTPs;
|
||||
@@ -455,7 +519,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'_message': function(aConnection, someParameters) {
|
||||
'_message': function(aConnection, someParameters, someOptionalParameters) {
|
||||
var result;
|
||||
|
||||
if (MochiKit.Base.keys(aConnection).length==0) {
|
||||
@@ -499,8 +563,14 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
|
||||
//=====================================================================
|
||||
} else if (someParameters.message == 'getRecordDetail') {
|
||||
MochiKit.Base.update(result, aConnection['userData']['records'][someParameters['parameters']['reference']]);
|
||||
result['reference'] = someParameters['parameters']['reference'];
|
||||
var reference = someParameters['parameters']['reference'];
|
||||
|
||||
MochiKit.Base.update(result, aConnection['userData']['records'][reference]);
|
||||
result['reference'] = reference;
|
||||
|
||||
result['attachmentStatus'] = this.attachmentStatus(aConnection, reference);
|
||||
|
||||
//console.log('Proxy.getRecordDetail', result);
|
||||
} else if (someParameters.message == 'getAllRecordDetails') {
|
||||
MochiKit.Base.update(result, aConnection['userData']['records']);
|
||||
} else if (someParameters.message == 'getOneTimePasswordsDetails') {
|
||||
@@ -613,7 +683,9 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
*/ //=====================================================================
|
||||
} else if (someParameters.message == 'saveChanges') {
|
||||
if (this.isReadOnly() == false) {
|
||||
var i, c;
|
||||
//console.log("Proxy.Offline.DataStore.saveChanges: parameters:", someParameters);
|
||||
//console.log("Proxy.Offline.DataStore.saveChanges: attachments:", aConnection['userData']['attachments']);
|
||||
var i, c, j, d;
|
||||
|
||||
if (aConnection['userData']['lock'] != someParameters['parameters']['user']['lock']) {
|
||||
throw "the lock attribute is not processed correctly"
|
||||
@@ -664,8 +736,12 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
'accessDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date())
|
||||
}
|
||||
}
|
||||
|
||||
this.pruneAttachments(aConnection, currentRecordData['record']['reference'], currentRecordData['attachments']);
|
||||
}
|
||||
|
||||
//console.log("Proxy.Offline.DataStore.saveChanges: attachments:", aConnection['userData']['attachments']);
|
||||
|
||||
c = someParameters['parameters']['records']['deleted'].length;
|
||||
for (i=0; i<c; i++) {
|
||||
var currentRecordReference;
|
||||
@@ -731,7 +807,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
},MochiKit.Base.values(aConnection['userOTPs']));
|
||||
} else {
|
||||
throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
|
||||
}
|
||||
}
|
||||
//=====================================================================
|
||||
//
|
||||
// U N H A N D L E D M e t h o d
|
||||
@@ -816,10 +892,47 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
'attachmentStatus': function(aConnection, aRecordReference) {
|
||||
var result;
|
||||
|
||||
//console.log("Proxy.attachmentStatus: data:", aConnection['userData']['attachments']);
|
||||
|
||||
result = {};
|
||||
if (aConnection['userData']['attachments']) {
|
||||
var reference;
|
||||
|
||||
for (reference in aConnection['userData']['attachments']) {
|
||||
//console.log("Proxy.attachmentStatus: -> comparison: serverRecordReference, aRecordReference", aConnection['userData']['attachments'][reference]['record'], aRecordReference);
|
||||
if (aConnection['userData']['attachments'][reference]['record'] == aRecordReference) {
|
||||
result[reference] = aConnection['userData']['attachments'][reference]['status'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Proxy.attachmentStatus: result:", result);
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/** Removes all the stored attachments whose reference is not in the given list */
|
||||
'pruneAttachments': function(aConnection, aRecordReference, someAttachmentReferences) {
|
||||
if (aConnection['userData']['attachments']) {
|
||||
var reference;
|
||||
for (reference in aConnection['userData']['attachments']) {
|
||||
var attachment = aConnection['userData']['attachments'][reference];
|
||||
if ( (attachment['record'] == aRecordReference) &&
|
||||
(someAttachmentReferences.indexOf(reference) < 0)
|
||||
) {
|
||||
delete aConnection['userData']['attachments'][reference];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
'userSerializedEncryptedData': function(someData) {
|
||||
var deferredResult;
|
||||
|
||||
@@ -53,10 +53,19 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline, Clipperz.PM.Proxy, {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'_sendMessage': function(aFunctionName, aVersion, someParameters) {
|
||||
return this.dataStore().processMessage(aFunctionName, someParameters);
|
||||
'_sendMessage': function(aFunctionName, aVersion, someParameters, someOptionalParameters) {
|
||||
return this.dataStore().processMessage(aFunctionName, someParameters, someOptionalParameters);
|
||||
},
|
||||
|
||||
'_uploadAttachment': function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
return this.dataStore().uploadAttachment(someArguments, aProgressCallback, aSharedSecret, aToll);
|
||||
},
|
||||
|
||||
'_downloadAttachment': function(someArguments, aProgressCallback, aSharedSecret, aToll) {
|
||||
return this.dataStore().downloadAttachment(someArguments, aProgressCallback, aSharedSecret, aToll);
|
||||
},
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
'isReadOnly': function () {
|
||||
|
||||
550
frontend/delta/js/Clipperz/PM/UI/AttachmentController.js
Normal file
550
frontend/delta/js/Clipperz/PM/UI/AttachmentController.js
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
Clipperz.Base.module('Clipperz.PM.UI');
|
||||
|
||||
Clipperz.PM.UI.AttachmentController = function(someParameters) {
|
||||
this.MAX_SIMULTANEOUS_READ = 1;
|
||||
this.MAX_SIMULTANEOUS_UPLOAD = 1;
|
||||
this.MAX_SIMULTANEOUS_DOWNLOAD = 1;
|
||||
this.MAX_SIMULTANEOUS_ENCRYPT = 1;
|
||||
this.MAX_SIMULTANEOUS_DECRYPT = 1;
|
||||
|
||||
this.LATEST_ENCRYPTION_VERSION = '1.0'; // Versions aren't handled completely yet!
|
||||
|
||||
this.fileQueue = [];
|
||||
this.notifications = [];
|
||||
this.operationsCount = null;
|
||||
|
||||
this.encryptedDocument = null;
|
||||
|
||||
this.uploadMessageCallback = someParameters['uploadMessageCallback'];
|
||||
this.downloadMessageCallback = someParameters['downloadMessageCallback'];
|
||||
this.reloadServerStatusCallback = someParameters['reloadServerStatusCallback'];
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
|
||||
|
||||
toString: function () {
|
||||
return "Clipperz.PM.UI.AttachmentController";
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
notifyUpdate: function() {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updateAttachmentQueueInfo', this.getQueueInfo(), this.getNotificationsInfo());
|
||||
},
|
||||
|
||||
getQueueInfo: function() {
|
||||
return this.fileQueue;
|
||||
},
|
||||
|
||||
getNotificationsInfo: function() {
|
||||
return this.notifications;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Entry points
|
||||
//=========================================================================
|
||||
|
||||
addAttachment: function (anAttachment) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadAttachment", {trace:false});
|
||||
deferredResult.collectResults({
|
||||
'_attachment': MochiKit.Base.partial(MochiKit.Async.succeed, anAttachment),
|
||||
'_record': MochiKit.Base.method(anAttachment, 'record'),
|
||||
'reference': MochiKit.Base.method(anAttachment, 'reference'),
|
||||
'meta': MochiKit.Base.method(anAttachment, 'metadata'),
|
||||
'key': MochiKit.Base.method(anAttachment, 'key'),
|
||||
'nonce': MochiKit.Base.method(anAttachment, 'nonce'),
|
||||
'status': MochiKit.Base.partial(MochiKit.Async.succeed, 'WAITING_READ'),
|
||||
'file': MochiKit.Base.method(anAttachment, 'file'),
|
||||
'recordReference': MochiKit.Base.method(anAttachment.record(), 'reference'),
|
||||
'process': MochiKit.Base.partial(MochiKit.Async.succeed, 'UPLOAD'), // Used only to differentiate notifications
|
||||
}, {trace: false});
|
||||
deferredResult.addMethod(this, 'addFileToQueue');
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
getAttachment: function(anAttachment, aMessageCallback) {
|
||||
if (this.getQueuePosition(anAttachment.reference()) >= 0) {
|
||||
this.removeFileFromQueue(anAttachment.reference());
|
||||
}
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.downloadAttachment", {trace:false});
|
||||
deferredResult.collectResults({
|
||||
'_attachment': MochiKit.Base.partial(MochiKit.Async.succeed, anAttachment),
|
||||
'_record': MochiKit.Base.method(anAttachment, 'record'),
|
||||
'reference': MochiKit.Base.method(anAttachment, 'reference'),
|
||||
'meta': MochiKit.Base.method(anAttachment, 'metadata'),
|
||||
'key': MochiKit.Base.method(anAttachment, 'key'),
|
||||
'nonce': MochiKit.Base.method(anAttachment, 'nonce'),
|
||||
'status': MochiKit.Base.partial(MochiKit.Async.succeed, 'WAITING_DOWNLOAD'),
|
||||
'messageCallback': MochiKit.Async.succeed(aMessageCallback),
|
||||
'process': MochiKit.Base.partial(MochiKit.Async.succeed, 'DOWNLOAD'), // Used only to differentiate notifications
|
||||
}, {trace: false});
|
||||
deferredResult.addCallback(function(aResult){
|
||||
MochiKit.Base.update(aResult, {'messageCallback': aMessageCallback});
|
||||
return aResult;
|
||||
});
|
||||
deferredResult.addMethod(this, 'addFileToQueue');
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
cancelAttachment: function(anAttachment) {
|
||||
var deferredResult;
|
||||
|
||||
var reference = anAttachment.reference()
|
||||
var queueElement = this.getQueueElement(reference);
|
||||
|
||||
if (queueElement) {
|
||||
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.cancelAttachment", {trace:false});
|
||||
deferredResult.addMethod(this, 'updateFileInQueue', reference, {'status': 'CANCELED'});
|
||||
if (queueElement['deferredRequest']) {
|
||||
deferredResult.addMethod(queueElement['deferredRequest'], 'cancel');
|
||||
}
|
||||
|
||||
|
||||
deferredResult.callback();
|
||||
// TODO: We may also want do delete stuff in the queue element
|
||||
} else {
|
||||
deferredResult = MochiKit.Async.succeed();
|
||||
}
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Queue management
|
||||
//=========================================================================
|
||||
|
||||
dispatchQueueOperations: function() {
|
||||
var currentElement;
|
||||
var processNextElements;
|
||||
|
||||
var count = this.updateOperationsCount();
|
||||
|
||||
this.notifyUpdate();
|
||||
|
||||
processNextElements = true;
|
||||
for (i in this.fileQueue) {
|
||||
if(processNextElements) {
|
||||
currentElement = this.fileQueue[i];
|
||||
switch (currentElement['status']) {
|
||||
case 'WAITING_READ':
|
||||
if ((count['READING']) < this.MAX_SIMULTANEOUS_READ) {
|
||||
this.readFile(currentElement['reference'], currentElement['file']);
|
||||
processNextElements = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'WAITING_ENCRYPT':
|
||||
if (count['ENCRYPTING'] < this.MAX_SIMULTANEOUS_ENCRYPT) {
|
||||
this.encryptFile(currentElement['reference'], currentElement['originalArray'], currentElement['key'], currentElement['nonce']);
|
||||
processNextElements = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'WAITING_UPLOAD':
|
||||
if (count['UPLOADING'] < this.MAX_SIMULTANEOUS_UPLOAD) {
|
||||
this.uploadFile(currentElement['reference'], currentElement['encryptedArray']);
|
||||
processNextElements = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'WAITING_DOWNLOAD':
|
||||
if (count['DOWNLOADING'] < this.MAX_SIMULTANEOUS_DOWNLOAD) {
|
||||
this.downloadFile(currentElement['reference'], currentElement['messageCallback']);
|
||||
processNextElements = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'WAITING_DECRYPT':
|
||||
if (count['DECRYPTING'] < this.MAX_SIMULTANEOUS_DECRYPT) {
|
||||
this.decryptFile(currentElement['reference'], currentElement['encryptedArray'], currentElement['key'], currentElement['nonce']);
|
||||
processNextElements = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'WAITING_SAVE':
|
||||
this.saveFile(currentElement['reference'], currentElement['decryptedArray'], currentElement['meta']['name'], currentElement['meta']['type']);
|
||||
processNextElements = false;
|
||||
Clipperz.Sound.beep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateOperationsCount: function() {
|
||||
var count;
|
||||
|
||||
count = {
|
||||
'WAITING_READ': 0,
|
||||
'READING': 0,
|
||||
'WAITING_ENCRYPT': 0,
|
||||
'ENCRYPTING': 0,
|
||||
'WAITING_UPLOAD': 0,
|
||||
'UPLOADING': 0,
|
||||
'WAITING_DOWNLOAD': 0,
|
||||
'DOWNLOADING': 0,
|
||||
'WAITING_DECRYPT': 0,
|
||||
'DECRYPTING': 0,
|
||||
'WAITING_SAVE': 0,
|
||||
'DONE': 0,
|
||||
'CANCELED': 0,
|
||||
'FAILED': 0,
|
||||
};
|
||||
|
||||
for (var i in this.fileQueue) {
|
||||
count[this.fileQueue[i]['status']]++;
|
||||
}
|
||||
|
||||
this.operationsCount = count;
|
||||
|
||||
return this.operationsCount;
|
||||
},
|
||||
|
||||
addFileToQueue: function(someParameters) {
|
||||
this.fileQueue.push(someParameters);
|
||||
this.addNotification(someParameters);
|
||||
|
||||
this.dispatchQueueOperations();
|
||||
},
|
||||
|
||||
removeFileFromQueue: function(aFileReference) {
|
||||
this.fileQueue.splice(this.getQueuePosition(aFileReference), 1);
|
||||
|
||||
this.dispatchQueueOperations();
|
||||
},
|
||||
|
||||
getQueueElement: function(aFileReference) {
|
||||
var i = this.getQueuePosition(aFileReference);
|
||||
return this.fileQueue[i];
|
||||
},
|
||||
|
||||
updateFileInQueue: function(aFileReference, someParameters) {
|
||||
var queuePosition = this.getQueuePosition(aFileReference);
|
||||
|
||||
MochiKit.Base.update(this.fileQueue[queuePosition], someParameters);
|
||||
|
||||
this.dispatchQueueOperations();
|
||||
},
|
||||
|
||||
appendResult: function(aFileReference, anArray) {
|
||||
var queueElement = this.getQueueElement(aFileReference);
|
||||
queueElement['result'].set(anArray, queueElement['currentByte']);
|
||||
},
|
||||
|
||||
getQueuePosition: function(aFileReference) {
|
||||
var result;
|
||||
|
||||
result = -1;
|
||||
for (var i in this.fileQueue) {
|
||||
if (this.fileQueue[i].reference == aFileReference) {
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing
|
||||
//=========================================================================
|
||||
|
||||
addNotification: function(aQueueElement) {
|
||||
this.notifications.push({
|
||||
'id': this.randomId(),
|
||||
'queueElement': aQueueElement
|
||||
})
|
||||
},
|
||||
|
||||
removeNotification: function(aNotificationId) {
|
||||
var i, position;
|
||||
|
||||
position = -1;
|
||||
for (i in this.notifications) {
|
||||
if (this.notifications[i]['id'] == aNotificationId) {
|
||||
position = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (position >= 0) {
|
||||
this.notifications.splice(position, 1);
|
||||
}
|
||||
|
||||
this.notifyUpdate();
|
||||
},
|
||||
|
||||
randomId: function() {
|
||||
return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: READ
|
||||
//=========================================================================
|
||||
|
||||
readFile: function(aFileReference, aFile) {
|
||||
var reader = new FileReader();
|
||||
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'READING',
|
||||
});
|
||||
|
||||
reader.onload = MochiKit.Base.method(this, 'readFileOnload', aFileReference);
|
||||
reader.readAsArrayBuffer(aFile);
|
||||
},
|
||||
|
||||
readFileOnload: function(aFileReference, anEvent) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'WAITING_ENCRYPT',
|
||||
'originalArray': new Uint8Array(anEvent.target.result),
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: ENCRYPT
|
||||
//=========================================================================
|
||||
|
||||
encryptFile: function(aFileReference, anArrayBuffer, aKey, aNonce) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'ENCRYPTING',
|
||||
});
|
||||
|
||||
window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
|
||||
{ name: "AES-CBC" }, //this is the algorithm options
|
||||
false, //whether the key is extractable (i.e. can be used in exportKey)
|
||||
["encrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
||||
)
|
||||
// .then(MochiKit.Base.method(this, 'doEncrypt', aFileReference, anArrayBuffer, aNonce))
|
||||
.then(this.doEncrypt.bind(this,aFileReference, anArrayBuffer, aNonce))
|
||||
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'encryptFile(): encryption failed'));
|
||||
},
|
||||
|
||||
doEncrypt: function(aFileReference, anArrayBuffer, anIV, aWebcryptoKey) {
|
||||
window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-CBC",
|
||||
iv: anIV,
|
||||
},
|
||||
aWebcryptoKey,
|
||||
anArrayBuffer
|
||||
)
|
||||
.then(MochiKit.Base.method(this, 'doneEncrypt', aFileReference))
|
||||
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'doEncrypt(): encryption failed'));
|
||||
},
|
||||
|
||||
doneEncrypt: function(aFileReference, anArrayBuffer) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'WAITING_UPLOAD',
|
||||
'encryptedArray': new Uint8Array(anArrayBuffer),
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: UPLOAD
|
||||
//=========================================================================
|
||||
|
||||
uploadFile: function(aFileReference, anEncryptedArray) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'UPLOADING',
|
||||
'deferredRequest': this.uploadFileRequest(aFileReference, anEncryptedArray),
|
||||
'requestProgress': 0,
|
||||
});
|
||||
},
|
||||
|
||||
uploadFileRequest: function(aFileReference, anEncryptedArray) {
|
||||
var deferredResult;
|
||||
var queueElement = this.getQueueElement(aFileReference);
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadFileRequest", {trace:false});
|
||||
deferredResult.addCallback(this.uploadMessageCallback, {
|
||||
'attachmentReference': queueElement['_attachment'].reference(),
|
||||
'recordReference': queueElement['_attachment'].record().reference(),
|
||||
'arrayBufferData': anEncryptedArray,
|
||||
'version': this.LATEST_ENCRYPTION_VERSION,
|
||||
}, MochiKit.Base.method(this, 'uploadFileProgress', aFileReference));
|
||||
deferredResult.addMethod(this, 'uploadFileDone', aFileReference);
|
||||
deferredResult.addErrback(MochiKit.Base.method(this, 'handleException', aFileReference, 'uploadFileRequest(): request failed or canceled'));
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
uploadFileDone: function(aFileReference, aResult){
|
||||
var record = this.getQueueElement(aFileReference)['_record'];
|
||||
|
||||
return Clipperz.Async.callbacks("AttachmentController.uploadFileDone", [
|
||||
MochiKit.Base.partial(this.reloadServerStatusCallback, record),
|
||||
MochiKit.Base.method(this, 'updateFileInQueue', aFileReference, {
|
||||
'status': 'DONE',
|
||||
'requestProgress': 1,
|
||||
}),
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
uploadFileProgress: function(aFileReference, anEvent) {
|
||||
var newProgress = (anEvent.lengthComputable) ? (anEvent.loaded / anEvent.total) : -1;
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'requestProgress': newProgress,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: DOWNLOAD
|
||||
//=========================================================================
|
||||
|
||||
downloadFile: function(aFileReference) {
|
||||
var deferredRequest;
|
||||
var queueElement = this.getQueueElement(aFileReference);
|
||||
|
||||
deferredRequest = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.downloadFile", {trace:false});
|
||||
deferredRequest.addCallback(this.downloadMessageCallback, queueElement['_attachment'], MochiKit.Base.method(this, 'downloadFileProgress', aFileReference));
|
||||
deferredRequest.addMethod(this, 'downloadFileDone', aFileReference);
|
||||
deferredRequest.addErrback(MochiKit.Base.method(this, 'handleException', aFileReference, 'downloadFile(): download filed or canceled'));
|
||||
deferredRequest.callback();
|
||||
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'DOWNLOADING',
|
||||
'deferredRequest': deferredRequest,
|
||||
'requestProgress': 0,
|
||||
});
|
||||
},
|
||||
|
||||
downloadFileDone: function(aFileReference, aResult){
|
||||
var queueElement = this.getQueueElement(aFileReference);
|
||||
var encryptedArray = new Uint8Array(aResult);
|
||||
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'WAITING_DECRYPT',
|
||||
'key': queueElement['key'],
|
||||
'nonce': queueElement['nonce'],
|
||||
'encryptedArray': encryptedArray,
|
||||
'requestProgress': 1,
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
downloadFileProgress: function(aFileReference, anEvent) {
|
||||
var newProgress = (anEvent.lengthComputable) ? (anEvent.loaded / anEvent.total) : -1;
|
||||
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'requestProgress': newProgress,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: DECRYPT
|
||||
//=========================================================================
|
||||
|
||||
decryptFile: function(aFileReference, anArrayBuffer, aKey, aNonce) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'DECRYPTING',
|
||||
});
|
||||
|
||||
window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
|
||||
{name: "AES-CBC"}, //this is the algorithm options
|
||||
false, //whether the key is extractable (i.e. can be used in exportKey)
|
||||
["decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
||||
)
|
||||
.then(MochiKit.Base.method(this, 'doDecrypt', aFileReference, anArrayBuffer, aNonce))
|
||||
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'decryptFile(): decryption failed'));
|
||||
},
|
||||
|
||||
doDecrypt: function(aFileReference, anArrayBuffer, anIV, aWebcryptoKey) {
|
||||
window.crypto.subtle.decrypt(
|
||||
{name: "AES-CBC", iv: anIV},
|
||||
aWebcryptoKey,
|
||||
anArrayBuffer
|
||||
)
|
||||
.then(MochiKit.Base.method(this, 'doneDecrypt', aFileReference))
|
||||
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'doDecrypt(): decryption failed'));
|
||||
},
|
||||
|
||||
doneDecrypt: function(aFileReference, anArrayBuffer) {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'WAITING_SAVE',
|
||||
'decryptedArray': new Uint8Array(anArrayBuffer),
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Queue Processing: SAVE
|
||||
//=========================================================================
|
||||
|
||||
saveFile: function(aFileReference, anArray, aFileName, aFileType) {
|
||||
var blob = new Blob([anArray], {type: aFileType});
|
||||
saveAs(blob, aFileName);
|
||||
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'DONE',
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Exceptions
|
||||
//=========================================================================
|
||||
|
||||
/** Handles exceptions for upload/download and encrypt/decrypt. Note that
|
||||
* an exception is thrown also when the user manually cancels the file
|
||||
* processing. In this case the status remains 'CANCELED'.
|
||||
*/
|
||||
handleException: function(aFileReference, aMessage) {
|
||||
var queueElement = this.getQueueElement(aFileReference);
|
||||
|
||||
if (queueElement['status'] != 'CANCELED') {
|
||||
this.updateFileInQueue(aFileReference, {
|
||||
'status': 'FAILED',
|
||||
});
|
||||
}
|
||||
|
||||
if (aMessage) {
|
||||
console.log("AttachmentController: caught exception (" + aMessage + ")");
|
||||
}
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
Clipperz.Base.module('Clipperz.PM.UI.Components');
|
||||
|
||||
Clipperz.PM.UI.Components.AttachmentQueueBoxClass = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
'attachmentQueueInfo': React.PropTypes.object.isRequired,
|
||||
'attachmentQueueBoxStatus': React.PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
'unreadNotifications': [],
|
||||
};
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Actions
|
||||
//=========================================================================
|
||||
|
||||
handleClose: function(aNotificationElement) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'closeAttachmentNotification', aNotificationElement['id']);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Render Methods
|
||||
//=========================================================================
|
||||
|
||||
renderNotificationElement: function(aNotificationElement) {
|
||||
var processIcon, status, closeButton, progressIndicator;
|
||||
|
||||
var queueElement = aNotificationElement['queueElement'];
|
||||
|
||||
processIcon = (queueElement['process'] == 'UPLOAD') ? "\u2b06" : "\u2b07";
|
||||
status = "waiting";
|
||||
closeButton = null;
|
||||
progressIndicator = null;
|
||||
if (queueElement['status'] == 'DOWNLOADING' || queueElement['status'] == 'UPLOADING') {
|
||||
status = Math.floor(queueElement['requestProgress']*100) + '%';
|
||||
progressIndicator = React.DOM.span({'className': 'progress'}, Clipperz.PM.UI.Components.RadialProgressIndicator({'progress': queueElement['requestProgress']}));
|
||||
}
|
||||
|
||||
if (queueElement['status'] == 'DONE' || queueElement['status'] == 'CANCELED' || queueElement['status'] == 'FAILED') {
|
||||
status = (queueElement['status'] == 'DONE') ? 'completed' : (queueElement['status'] == 'FAILED') ? "failed" : "canceled";
|
||||
|
||||
closeButton = React.DOM.span({'className': 'close'}, React.DOM.a({
|
||||
'className': 'close',
|
||||
'onClick': MochiKit.Base.method(this, 'handleClose', aNotificationElement),
|
||||
}, "remove field"));
|
||||
}
|
||||
|
||||
return React.DOM.li({},[
|
||||
React.DOM.span({'className': 'contentType'}, Clipperz.PM.DataModel.Attachment.contentTypeIcon(queueElement['meta']['type'])),
|
||||
React.DOM.span({'className': 'name'}, queueElement['meta']['name']),
|
||||
// React.DOM.span({'className': 'size'}, queueElement['meta']['size']),
|
||||
React.DOM.span({'className': 'status'}, [
|
||||
React.DOM.span({'className': 'statusString'}, status),
|
||||
React.DOM.span({'className': 'processIcon'}, processIcon),
|
||||
]),
|
||||
progressIndicator,
|
||||
closeButton,
|
||||
]);
|
||||
},
|
||||
|
||||
renderNotifications: function(someNotifications) {
|
||||
var result;
|
||||
|
||||
if (someNotifications.length == 0) {
|
||||
result = React.DOM.p({}, "No attachments in queue");
|
||||
} else {
|
||||
result = MochiKit.Base.map(MochiKit.Base.method(this, 'renderNotificationElement'), someNotifications);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// renderNotifications: function(someNotifications) {
|
||||
// console.log('AttachmentQueueBox.renderNotifications:', someNotifications);
|
||||
// },
|
||||
|
||||
render: function () {
|
||||
//test
|
||||
this.renderNotifications(this.props['attachmentQueueInfo']['notifications']);
|
||||
|
||||
return React.DOM.div({
|
||||
'className': 'attachmentQueueStatus '+this.props['attachmentQueueBoxStatus'],
|
||||
}, [
|
||||
React.DOM.div({'className': 'arrow'}),
|
||||
React.DOM.ul({}, this.renderNotifications(this.props['attachmentQueueInfo']['notifications'])),
|
||||
]);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
});
|
||||
|
||||
Clipperz.PM.UI.Components.AttachmentQueueBox = React.createFactory(Clipperz.PM.UI.Components.AttachmentQueueBoxClass);
|
||||
@@ -30,22 +30,40 @@ Clipperz.PM.UI.Components.ButtonClass = React.createClass({
|
||||
'eventName': React.PropTypes.string.isRequired,
|
||||
'label': React.PropTypes.string.isRequired,
|
||||
'handler': React.PropTypes.func.isRequired,
|
||||
'className': React.PropTypes.string
|
||||
'className': React.PropTypes.string,
|
||||
'badgeTopContent': React.PropTypes.string,
|
||||
'badgeBottomContent': React.PropTypes.string,
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
render: function () {
|
||||
var badgeTop;
|
||||
var badgeBottom;
|
||||
|
||||
var classes = {
|
||||
'button': true
|
||||
};
|
||||
if (typeof(this.props['className']) != 'undefined') {
|
||||
classes[this.props['className']] = true;
|
||||
};
|
||||
|
||||
badgeTop = null;
|
||||
if (this.props['badgeTopContent']) {
|
||||
badgeTop = React.DOM.span({'className': 'badge top'}, this.props['badgeTopContent']);
|
||||
};
|
||||
|
||||
badgeBottom = null;
|
||||
if (this.props['badgeBottomContent']) {
|
||||
badgeBottom = React.DOM.span({'className': 'badge bottom'}, this.props['badgeBottomContent']);
|
||||
};
|
||||
|
||||
|
||||
return React.DOM.div({className:Clipperz.PM.UI.Components.classNames(classes), onClick:this.props['handler']}, [
|
||||
React.DOM.div({className:this.props['eventName']}, [
|
||||
React.DOM.h3({className:'label'}, this.props['label'])
|
||||
React.DOM.h3({className:'label'}, this.props['label']),
|
||||
badgeTop,
|
||||
badgeBottom,
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
|
||||
'accountInfo': React.PropTypes.object.isRequired,
|
||||
'proxyInfo': React.PropTypes.object.isRequired,
|
||||
'messageBox': React.PropTypes.object.isRequired,
|
||||
'filter': React.PropTypes.object /*.isRequired */
|
||||
'filter': React.PropTypes.object /*.isRequired */,
|
||||
'attachmentQueueInfo': React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -48,15 +49,31 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleSettingsPanel');
|
||||
},
|
||||
|
||||
attachmentQueueToggleHandler: function(anEvent) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleAttachmentQueueBox');
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
renderWithSidePanels: function () {
|
||||
var attachmentDownloadNotificationNumber = MochiKit.Base.filter(function(anElement) {
|
||||
return anElement['queueElement']['process'] == 'DOWNLOAD';
|
||||
}, this.props['attachmentQueueInfo']['notifications']).length;
|
||||
|
||||
var attachmentUploadNotificationNumber = this.props['attachmentQueueInfo']['notifications'].length - attachmentDownloadNotificationNumber;
|
||||
|
||||
return [
|
||||
React.DOM.div({className:'selectionToggle'}, [
|
||||
Clipperz.PM.UI.Components.Button({eventName:'selectionToggleButton', label:"tags", handler:this.selectionToggleHandler})
|
||||
]),
|
||||
this.renderWithoutSidePanels(),
|
||||
// React.DOM.div({className:'attachmentToggle'}, [
|
||||
// Clipperz.PM.UI.Components.Button({eventName:'attachmentQueueToggleButton', label:"clipperz", handler:this.attachmentQueueToggleHandler})
|
||||
// ]),
|
||||
|
||||
// TODO: validate and adjust names
|
||||
React.DOM.div({className:'settingsToggle'}, [
|
||||
Clipperz.PM.UI.Components.Button({eventName:'attachmentQueueToggleButton', label:"\u2191\u2193", handler:this.attachmentQueueToggleHandler, badgeTopContent: attachmentDownloadNotificationNumber, badgeBottomContent: attachmentUploadNotificationNumber}),
|
||||
Clipperz.PM.UI.Components.Button({eventName:'settingsToggleButton', label:"menu", handler:this.settingsToggleHandler})
|
||||
])
|
||||
];
|
||||
@@ -70,6 +87,8 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
|
||||
|
||||
if (this.props['filter']['type'] == 'RECENT') {
|
||||
result = [React.DOM.div({className:'clipperz'}, [React.DOM.span({className:'logo recent'}, "recent")])];
|
||||
} else if (this.props['filter']['type'] == 'WITH_ATTACHMENTS') {
|
||||
result = [React.DOM.div({className:'clipperz'}, [React.DOM.span({className:'logo withAttachments'}, "attachment")])];
|
||||
} else if (this.props['filter']['type'] == 'TAG') {
|
||||
result = [React.DOM.div({className:'clipperz'}, [
|
||||
React.DOM.span({className:'logo tag'}, "tag"),
|
||||
|
||||
@@ -136,6 +136,7 @@ Clipperz.PM.UI.Components.Cards.CommandToolbarClass = React.createClass({
|
||||
var classes = {
|
||||
'cardDetailToolbar': true,
|
||||
'commands': true,
|
||||
'top': true,
|
||||
};
|
||||
classes[style] = true;
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ Clipperz.PM.UI.Components.Cards.DetailClass = React.createClass({
|
||||
result['showGlobalMask'] = this.props['showGlobalMask'];
|
||||
result['allTags'] = this.props['allTags'];
|
||||
result['preferences'] = this.props['preferences'];
|
||||
result['attachmentQueueInfo'] = this.props['attachmentQueueInfo'];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -41,6 +41,8 @@ Clipperz.PM.UI.Components.Cards.EditClass = React.createClass({
|
||||
'fromFieldPosition': -1,
|
||||
'toFieldPosition': -1,
|
||||
'dropPosition': -1,
|
||||
|
||||
'skippedFiles': [],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -581,7 +583,14 @@ console.log("DROP"); //, anEvent);
|
||||
},
|
||||
|
||||
renderAddNewField: function () {
|
||||
return React.DOM.div({'className':'newCardField', 'onClick':this.addNewField}, "add new field");
|
||||
// return React.DOM.div({'className':'newCardField', 'onClick':this.addNewField}, "add new field");
|
||||
return React.DOM.div({'className':'newCardField', 'onClick':this.addNewField}, [
|
||||
React.DOM.div({'className':'fieldGhostShadow'}, [
|
||||
React.DOM.div({'className':'label'}, ""),
|
||||
React.DOM.div({'className':'value'}, ""),
|
||||
]),
|
||||
React.DOM.div({'className':'addNewFieldButton'}, "add new field"),
|
||||
]);
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
@@ -603,6 +612,210 @@ console.log("DROP"); //, anEvent);
|
||||
|
||||
//............................................................................
|
||||
|
||||
handleRemoveAttachment: function(anAttachment) {
|
||||
// MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', anAttachment);
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'removeAttachment', this.record(), anAttachment);
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
uploadFiles: function(someFiles) {
|
||||
var i;
|
||||
|
||||
var newSkippedFiles = [];
|
||||
|
||||
for (i = 0; i < someFiles.length; i++) {
|
||||
var file = someFiles[i];
|
||||
|
||||
if (file.size <= Clipperz.PM.DataModel.Attachment.MAX_ATTACHMENT_SIZE) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'addAttachment', this.record(), file);
|
||||
} else {
|
||||
newSkippedFiles.push(file);
|
||||
this.setState({'skippedFiles': newSkippedFiles});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check compatibility with all browsers
|
||||
this.refs['attachmentInput'].getDOMNode().value = null;
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
handleFileSelect: function(anEvent) {
|
||||
this.uploadFiles(anEvent.target.files);
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
handleOnDrop: function (anEvent) {
|
||||
anEvent.preventDefault();
|
||||
|
||||
this.uploadFiles(anEvent.dataTransfer.files);
|
||||
},
|
||||
|
||||
handleOnDragOver: function (anEvent) {
|
||||
// Somehow necessary:
|
||||
// http://enome.github.io/javascript/2014/03/24/drag-and-drop-with-react-js.html
|
||||
// https://code.google.com/p/chromium/issues/detail?id=168387
|
||||
// http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
|
||||
anEvent.preventDefault();
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
renderSkippedFiles: function() {
|
||||
var result;
|
||||
|
||||
result = null;
|
||||
if (this.state['skippedFiles'].length > 0) {
|
||||
result = React.DOM.div({'className': 'skippedFiles'},[
|
||||
React.DOM.p({}, "The following files exceed the size limit of " + filesize(Clipperz.PM.DataModel.Attachment.MAX_ATTACHMENT_SIZE)),
|
||||
React.DOM.ul({},
|
||||
MochiKit.Base.map(function(aFile) {
|
||||
return React.DOM.li({}, [
|
||||
React.DOM.span({'className': 'filename'}, aFile.name),
|
||||
React.DOM.span({}, " (" + filesize(aFile.size) + ")"),
|
||||
]);
|
||||
}, this.state['skippedFiles'])
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachmentProgress: function(aStatus, aServerStatus, aProgress) {
|
||||
var result;
|
||||
|
||||
var broken = (! aServerStatus && (! aStatus || aStatus == 'CANCELED' || aStatus == 'FAILED' || aStatus == 'DONE'));
|
||||
|
||||
result = null;
|
||||
if (aStatus == 'UPLOADING' || aStatus == 'DOWNLOADING') {
|
||||
result = Clipperz.PM.UI.Components.RadialProgressIndicator({
|
||||
'progress': aProgress,
|
||||
'border': 1
|
||||
});
|
||||
} else if (! broken && aStatus != 'DONE' && aStatus != 'FAILED' && aServerStatus != 'AVAILABLE') {
|
||||
result = Clipperz.PM.UI.Components.RadialProgressIndicator({
|
||||
'progress': 0,
|
||||
'border': 1,
|
||||
'additionalClasses': ['waiting'],
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachmentStatus: function(aStatus, aServerStatus, aProgress) {
|
||||
var result;
|
||||
|
||||
var status = aStatus ? aStatus : false;
|
||||
|
||||
result = null;
|
||||
|
||||
if (status == 'FAILED') {
|
||||
result = React.DOM.span({'className': 'failed'}, "failed");
|
||||
} else if (status == 'UPLOADING' || status == 'DOWNLOADING') {
|
||||
var actionSymbol = (status == 'UPLOADING') ? "\u2b06" : "\u2b07";
|
||||
result = React.DOM.span({'className': 'progressStatus'}, actionSymbol + Math.floor(aProgress*100) + '%');
|
||||
} else if (aServerStatus != 'AVAILABLE') {
|
||||
switch(status) {
|
||||
case 'CANCELED':
|
||||
result = React.DOM.span({'className': 'broken'}, "canceled");
|
||||
break;
|
||||
case 'DONE':
|
||||
result = React.DOM.span({'className': 'broken'}, "failed");
|
||||
break;
|
||||
case false:
|
||||
result = React.DOM.span({'className': 'broken'}, "failed");
|
||||
break;
|
||||
default:
|
||||
result = React.DOM.span({'className': 'waiting'}, "waiting");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachmentActions: function(aStatus, aServerStatus, anAttachment) {
|
||||
var result;
|
||||
|
||||
result = null;
|
||||
if (aStatus != 'DOWNLOADING') {
|
||||
result = React.DOM.a({
|
||||
'className': 'remove',
|
||||
'onClick': MochiKit.Base.method(this, 'handleRemoveAttachment', anAttachment),
|
||||
}, "remove field");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachment: function(anAttachment) {
|
||||
var queueInfo = this.props['attachmentQueueInfo'].elementFetchCallback(anAttachment._reference) || [];
|
||||
var queueStatus = queueInfo['status'];
|
||||
var serverStatus = this.props['attachmentServerStatus'][anAttachment._reference];
|
||||
var broken = (! serverStatus && (! queueStatus || queueStatus == 'CANCELED' || queueStatus == 'FAILED' || queueStatus == 'DONE'));
|
||||
|
||||
// console.log(anAttachment['name'], queueStatus)
|
||||
|
||||
var status = this.renderAttachmentStatus(queueStatus, serverStatus, queueInfo['requestProgress']);
|
||||
var actions = this.renderAttachmentActions(queueStatus, serverStatus, anAttachment['_attachment']);
|
||||
var progressIndicator = this.renderAttachmentProgress(queueStatus, serverStatus, queueInfo['requestProgress']);;
|
||||
|
||||
return React.DOM.li({
|
||||
'className': (broken) ? 'broken' : '',
|
||||
'key': anAttachment._reference
|
||||
}, [
|
||||
React.DOM.span({'className': 'contentType'}, Clipperz.PM.DataModel.Attachment.contentTypeIcon(anAttachment.contentType)),
|
||||
React.DOM.span({'className': 'meta'}, [
|
||||
React.DOM.span({'className': 'name'}, anAttachment.name),
|
||||
React.DOM.span({'className': 'size'}, filesize(anAttachment.size)),
|
||||
]),
|
||||
React.DOM.span({'className': 'status'}, status),
|
||||
React.DOM.span({'className': 'progress'}, progressIndicator),
|
||||
React.DOM.span({'className': 'actions'}, actions),
|
||||
])
|
||||
},
|
||||
|
||||
renderAttachments: function(someAttachments) {
|
||||
return React.DOM.div({'className':'cardAttachmentWrapper'}, [
|
||||
React.DOM.div({'className': 'cardAttachments'}, [
|
||||
React.DOM.h3({'className': 'summaryText'}, "Attachments"),
|
||||
// React.DOM.p({'className': 'summaryText'}, someAttachments.length + ' files attached'),
|
||||
this.renderSkippedFiles(),
|
||||
React.DOM.ul({'className': 'attachmentList'},
|
||||
MochiKit.Base.map(MochiKit.Base.method(this, 'renderAttachment'), someAttachments)
|
||||
)
|
||||
]),
|
||||
React.DOM.div({
|
||||
'className': 'cardUploadAttachments',
|
||||
'onClick': MochiKit.Base.bind(function() { this.refs['attachmentInput'].getDOMNode().click() }, this),
|
||||
'onDragOver': this.handleOnDragOver,
|
||||
'onDrop': this.handleOnDrop,
|
||||
},[
|
||||
React.DOM.p({}, "Drag and drop your files here"),
|
||||
React.DOM.p({}, "or"),
|
||||
React.DOM.input({
|
||||
'type': 'file',
|
||||
'id': 'attachmentInput',
|
||||
'className': 'attachmentInput',
|
||||
'name': 'attachmentInput',
|
||||
'ref': 'attachmentInput',
|
||||
'onChange': this.handleFileSelect,
|
||||
'multiple': true
|
||||
}),
|
||||
React.DOM.a({
|
||||
'className': 'button',
|
||||
'onDragOver': this.handleOnDragOver,
|
||||
'onDrop': this.handleOnDrop,
|
||||
}, "select files"),
|
||||
])
|
||||
])
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
render: function () {
|
||||
var classes = {
|
||||
'edit': true
|
||||
@@ -618,8 +831,9 @@ console.log("DROP"); //, anEvent);
|
||||
this.renderTags(this.props['tags']),
|
||||
this.renderFields(this.fields()),
|
||||
this.renderAddNewField(),
|
||||
this.renderAttachments(MochiKit.Base.values(this.props['attachments'])),
|
||||
this.renderNotes(this.props['notes']),
|
||||
this.renderDirectLogins(this.props['directLogins'])
|
||||
this.renderDirectLogins(this.props['directLogins']),
|
||||
])
|
||||
]),
|
||||
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null
|
||||
|
||||
@@ -63,6 +63,7 @@ Clipperz.PM.UI.Components.Cards.ListClass = React.createClass({
|
||||
result = React.DOM.li({'className':Clipperz.PM.UI.Components.classNames(classes), 'onClick': this.handleClick, 'key':anItem['_reference'], 'data-reference':anItem['_reference'], 'data-label':anItem['label']}, [
|
||||
// React.DOM.span({'className':'favicon'}, Clipperz.PM.UI.Components.Cards.FavIcon({'src':anItem['favicon']})),
|
||||
React.DOM.span({'className':'label'}, anItem['label']),
|
||||
React.DOM.span({'className':'attachmentsCount'}, anItem['attachmentsCount'] ? 'attachment' : ''),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ Clipperz.PM.UI.Components.Cards.TagEditorClass = React.createClass({
|
||||
|
||||
renderEditField: function () {
|
||||
return [
|
||||
React.DOM.input({'type':'text', 'list':'tagListData', 'onKeyDown':this.handleKeyDown, 'onBlur':this.handleBlur, 'placeholder': "tag"}),
|
||||
// React.DOM.input({'type':'text', 'list':'tagListData', 'onKeyDown':this.handleKeyDown, 'onBlur':this.handleBlur, 'placeholder': "tag"}),
|
||||
React.DOM.datalist({'id':'tagListData'}, MochiKit.Base.map(function (aTag) { return React.DOM.option({}, aTag); }, this.listOfTagsNotUsedYet()))
|
||||
];
|
||||
},
|
||||
@@ -145,6 +145,7 @@ Clipperz.PM.UI.Components.Cards.TagEditorClass = React.createClass({
|
||||
return React.DOM.div({'className':Clipperz.PM.UI.Components.classNames(classes)}, [
|
||||
React.DOM.ul({},[
|
||||
MochiKit.Base.map(this.renderTag, this.props['selectedTags']),
|
||||
this.isReadOnly() ? null : React.DOM.li({}, React.DOM.input({'type':'text', 'list':'tagListData', 'onKeyDown':this.handleKeyDown, 'onBlur':this.handleBlur, 'placeholder': "tag"})),
|
||||
]),
|
||||
this.isReadOnly() ? null : this.renderEditField()
|
||||
]);
|
||||
|
||||
@@ -63,6 +63,7 @@ Clipperz.PM.UI.Components.Cards.TextAreaClass = React.createClass({
|
||||
handleKeyDown: function (anEvent) {
|
||||
switch (anEvent.keyCode) {
|
||||
case 27: // escape
|
||||
// console.log("ESCAPE");
|
||||
Mousetrap.trigger('esc');
|
||||
break;
|
||||
}
|
||||
@@ -78,7 +79,7 @@ Clipperz.PM.UI.Components.Cards.TextAreaClass = React.createClass({
|
||||
recalculateSize_1: function () {
|
||||
var node = this.getDOMNode();
|
||||
|
||||
node.style.height = 'auto';
|
||||
node.style.height = '33px';
|
||||
node.style.height = node.scrollHeight + 'px';
|
||||
window.scrollTo(window.scrollLeft, (node.scrollTop + node.scrollHeight));
|
||||
},
|
||||
|
||||
@@ -116,6 +116,17 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
|
||||
return result;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
handleGetAttachment: function (anAttachment) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'getAttachment', anAttachment);
|
||||
},
|
||||
|
||||
handleCancelDownload: function (anAttachment) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', anAttachment);
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'closeAttachment', anAttachment);
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
renderEmpty: function () {
|
||||
@@ -251,6 +262,132 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
|
||||
|
||||
//............................................................................
|
||||
|
||||
renderAttachmentProgress: function(aStatus, aServerStatus, aProgress) {
|
||||
var result;
|
||||
|
||||
var broken = (! aServerStatus && (! aStatus || aStatus == 'CANCELED' || aStatus == 'FAILED' || aStatus == 'DONE'));
|
||||
|
||||
result = null;
|
||||
if (aStatus == 'UPLOADING' || aStatus == 'DOWNLOADING') {
|
||||
result = Clipperz.PM.UI.Components.RadialProgressIndicator({
|
||||
'progress': aProgress,
|
||||
'border': 1
|
||||
});
|
||||
} else if (! broken && aStatus != 'DONE' && aServerStatus != 'AVAILABLE') {
|
||||
result = Clipperz.PM.UI.Components.RadialProgressIndicator({
|
||||
'progress': 0,
|
||||
'border': 1,
|
||||
'additionalClasses': ['waiting'],
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachmentStatus: function(aStatus, aServerStatus, aProgress) {
|
||||
var result;
|
||||
|
||||
var status = aStatus ? aStatus : false;
|
||||
|
||||
result = null;
|
||||
|
||||
if (status == 'FAILED') {
|
||||
result = React.DOM.span({'className': 'failed'}, "failed");
|
||||
} else if (status == 'UPLOADING' || status == 'DOWNLOADING') {
|
||||
var actionSymbol = (status == 'UPLOADING') ? "\u2b06" : "\u2b07";
|
||||
result = React.DOM.span({'className': 'progressStatus'}, actionSymbol + Math.floor(aProgress*100) + '%');
|
||||
} else if (aServerStatus != 'AVAILABLE') {
|
||||
switch(status) {
|
||||
case 'CANCELED':
|
||||
result = React.DOM.span({'className': 'broken'}, "canceled");
|
||||
break;
|
||||
case 'DONE':
|
||||
result = React.DOM.span({'className': 'broken'}, "failed");
|
||||
break;
|
||||
case false:
|
||||
result = React.DOM.span({'className': 'broken'}, "failed");
|
||||
break;
|
||||
default:
|
||||
result = React.DOM.span({'className': 'waiting'}, "waiting");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachmentActions: function(aStatus, aServerStatus, anAttachment) {
|
||||
var result;
|
||||
|
||||
result = null;
|
||||
if (aStatus == 'DOWNLOADING') {
|
||||
result = React.DOM.a({
|
||||
'className': 'cancel',
|
||||
'onClick': MochiKit.Base.method(this, 'handleCancelDownload', anAttachment)
|
||||
}, "remove field");
|
||||
} else if (aServerStatus == 'AVAILABLE') {
|
||||
result = React.DOM.a({
|
||||
'className': 'download',
|
||||
'onClick': MochiKit.Base.method(this, 'handleGetAttachment', anAttachment),
|
||||
}, "\u2b07");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachment: function (anAttachment) {
|
||||
var result;
|
||||
|
||||
if (this.props['attachmentQueueInfo'].elementFetchCallback != null) {
|
||||
var queueInfo = this.props['attachmentQueueInfo'].elementFetchCallback(anAttachment._reference) || [];
|
||||
var queueStatus = queueInfo['status'];
|
||||
var serverStatus = this.props['attachmentServerStatus'][anAttachment._reference];
|
||||
var broken = (! serverStatus && (! queueStatus || queueStatus == 'CANCELED'));
|
||||
|
||||
var status = this.renderAttachmentStatus(queueStatus, serverStatus, queueInfo['requestProgress']);
|
||||
var actions = this.renderAttachmentActions(queueStatus, serverStatus, anAttachment['_attachment']);
|
||||
var progressIndicator = this.renderAttachmentProgress(queueStatus, serverStatus, queueInfo['requestProgress']);
|
||||
|
||||
result = React.DOM.li({
|
||||
'className': (broken) ? 'broken' : '',
|
||||
'key': anAttachment._reference
|
||||
}, [
|
||||
React.DOM.span({'className': 'contentType'}, Clipperz.PM.DataModel.Attachment.contentTypeIcon(anAttachment.contentType)),
|
||||
React.DOM.span({'className': 'meta'}, [
|
||||
React.DOM.span({'className': 'name'}, anAttachment.name),
|
||||
React.DOM.span({'className': 'size'}, filesize(anAttachment.size)),
|
||||
]),
|
||||
React.DOM.span({'className': 'status'}, status),
|
||||
React.DOM.span({'className': 'progress'}, progressIndicator),
|
||||
React.DOM.span({'className': 'actions'}, actions),
|
||||
])
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderAttachments: function(someAttachments) {
|
||||
var result;
|
||||
|
||||
//console.log("View props:", this.props, someAttachments);
|
||||
if (someAttachments.length > 0) {
|
||||
result = React.DOM.div({'className': 'cardAttachments'}, [
|
||||
React.DOM.h3({'className': 'summaryText'}, "Attachments"),
|
||||
// React.DOM.p({'className': 'summaryText'}, someAttachments.length + ' files attached'),
|
||||
React.DOM.ul({'className': 'attachmentList'},
|
||||
MochiKit.Base.map(MochiKit.Base.method(this, 'renderAttachment'), someAttachments)
|
||||
)
|
||||
]);
|
||||
} else {
|
||||
result = [];
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
//............................................................................
|
||||
|
||||
renderCard: function () {
|
||||
var classes = {
|
||||
'view': true,
|
||||
@@ -263,8 +400,9 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
|
||||
this.renderLabel(this.props['label']),
|
||||
this.renderTags(this.props['tags']),
|
||||
this.renderFields(this.props['fields']),
|
||||
this.renderAttachments(MochiKit.Base.values(this.props['attachments'])),
|
||||
this.renderNotes(this.props['notes']),
|
||||
this.renderDirectLogins(this.props['directLogins'])
|
||||
this.renderDirectLogins(this.props['directLogins']),
|
||||
]),
|
||||
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
Clipperz.Base.module('Clipperz.PM.UI.Components');
|
||||
|
||||
Clipperz.PM.UI.Components.DocumentEncryptionSandboxClass = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
'documentMeta': null,
|
||||
'encryptedDocument': null,
|
||||
};
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Event Handlers
|
||||
//=========================================================================
|
||||
|
||||
handleFileSelect: function(anEvent) {
|
||||
// console.log("handleFileSelect: files:", anEvent.target.files);
|
||||
|
||||
var fileToRead = anEvent.target.files[0];
|
||||
|
||||
// console.log(fileToRead);
|
||||
// return;
|
||||
|
||||
if (fileToRead) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'uploadNewFile', null, fileToRead);
|
||||
}
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// File Handling Methods
|
||||
//=========================================================================
|
||||
// handleEncryptionSynchronously: function(aKey, aByteArray) {
|
||||
// var encryptedDocument;
|
||||
// var startTime, endTime;
|
||||
|
||||
// console.log("Encrypting...");
|
||||
// startTime = Date.now()
|
||||
// encryptedDocument = Clipperz.Crypto.AES.encrypt(aKey, aByteArray);
|
||||
// endTime = Date.now();
|
||||
// console.log("Finished! Encryption took", (endTime-startTime)/1000, "seconds.");
|
||||
|
||||
// this.setState({
|
||||
// 'encryptedDocument': encryptedDocument,
|
||||
// });
|
||||
// },
|
||||
|
||||
downloadFile: function(aFileReference) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadFile', aFileReference);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
// Render Methods
|
||||
//=========================================================================
|
||||
|
||||
renderUploadButton: function() {
|
||||
var result;
|
||||
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
result = React.DOM.input({
|
||||
'type': 'file',
|
||||
'id': 'files',
|
||||
'name': 'files',
|
||||
'onChange': this.handleFileSelect,
|
||||
'multiple': false
|
||||
})
|
||||
} else {
|
||||
result = React.DOM.p({}, "Browser doesn't support the FileReader API");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
renderQueue: function() {
|
||||
return MochiKit.Base.map(MochiKit.Base.bind(function(anElement) {
|
||||
return React.DOM.li({},[
|
||||
React.DOM.ul({}, [
|
||||
React.DOM.li({}, anElement['meta']['name']),
|
||||
React.DOM.li({}, anElement['meta']['size']),
|
||||
React.DOM.li({}, anElement['status']),
|
||||
React.DOM.li({}, Math.floor((anElement['currentByte']/anElement['meta']['size'])*100)+'%'),
|
||||
React.DOM.li({}, React.DOM.button({
|
||||
'disabled': ! anElement.isDataReady,
|
||||
'onClick': MochiKit.Base.method(this, 'downloadFile', anElement['reference']),
|
||||
}, "Download File")),
|
||||
])
|
||||
|
||||
]);
|
||||
}, this), this.props['attachmentQueueInfo']);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return React.DOM.div({
|
||||
'style': {
|
||||
'position': 'fixed',
|
||||
'width': '80%',
|
||||
'height': '80%',
|
||||
'background': 'white',
|
||||
'color': 'black',
|
||||
'top': '10%',
|
||||
'left': '10%',
|
||||
'border': '2px solid black',
|
||||
'zIndex': '10'
|
||||
}
|
||||
}, [
|
||||
React.DOM.p({}, "This is the doc encryption proof of concept."),
|
||||
React.DOM.p({}, "*Status*: working streaming encryption and decryption! Current size limit is 250MB"),
|
||||
this.renderUploadButton(),
|
||||
React.DOM.ul({},this.renderQueue()),
|
||||
]);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
});
|
||||
|
||||
Clipperz.PM.UI.Components.DocumentEncryptionSandbox = React.createFactory(Clipperz.PM.UI.Components.DocumentEncryptionSandboxClass);
|
||||
@@ -135,10 +135,10 @@ Clipperz.PM.UI.Components.ExtraFeatures.OTPClass = React.createClass({
|
||||
'</html>'
|
||||
);
|
||||
|
||||
newWindow.document.close();
|
||||
newWindow.focus();
|
||||
newWindow.print();
|
||||
newWindow.close();
|
||||
// newWindow.document.close();
|
||||
// newWindow.focus();
|
||||
// newWindow.print();
|
||||
// newWindow.close();
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -34,17 +34,18 @@ Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
'tags': React.PropTypes.object,
|
||||
'allTags': React.PropTypes.array,
|
||||
'messageBox': React.PropTypes.object.isRequired,
|
||||
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
|
||||
'features': React.PropTypes.array.isRequired,
|
||||
'userInfo': React.PropTypes.object.isRequired,
|
||||
'accountInfo': React.PropTypes.object.isRequired,
|
||||
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
|
||||
'locked': React.PropTypes.bool,
|
||||
// 'mediaQueryStyle': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
|
||||
// 'cards': React.PropTypes.deferred.isRequired
|
||||
'tags': React.PropTypes.object,
|
||||
'allTags': React.PropTypes.array,
|
||||
'messageBox': React.PropTypes.object.isRequired,
|
||||
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
|
||||
'features': React.PropTypes.array.isRequired,
|
||||
'userInfo': React.PropTypes.object.isRequired,
|
||||
'accountInfo': React.PropTypes.object.isRequired,
|
||||
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
|
||||
'locked': React.PropTypes.bool,
|
||||
'attachmentQueueInfo': React.PropTypes.object.isRequired,
|
||||
// 'mediaQueryStyle': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
|
||||
// 'cards': React.PropTypes.deferred.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
@@ -67,10 +68,11 @@ Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({
|
||||
classes[this.props['style']] = true;
|
||||
|
||||
result = React.DOM.div({'key':'mainPage', 'className':Clipperz.PM.UI.Components.classNames(classes)}, [
|
||||
Clipperz.PM.UI.Components.AttachmentQueueBox(this.props),
|
||||
this.props['style'] != 'extra-wide' ? Clipperz.PM.UI.Components.Panels.SelectionPanel(this.props) : null,
|
||||
Clipperz.PM.UI.Components.Panels.MainPanel(this.props),
|
||||
Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel(this.props),
|
||||
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null
|
||||
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,12 @@ Clipperz.PM.UI.Components.Panels.MainPanelClass = React.createClass({
|
||||
//=========================================================================
|
||||
|
||||
propTypes: {
|
||||
'allTags': React.PropTypes.array,
|
||||
'messageBox': React.PropTypes.object.isRequired,
|
||||
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
|
||||
'features': React.PropTypes.array.isRequired,
|
||||
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
|
||||
'allTags': React.PropTypes.array,
|
||||
'messageBox': React.PropTypes.object.isRequired,
|
||||
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
|
||||
'features': React.PropTypes.array.isRequired,
|
||||
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
|
||||
'attachmentQueueInfo': React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
style: function () {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
Clipperz.Base.module('Clipperz.PM.UI.Components');
|
||||
|
||||
Clipperz.PM.UI.Components.RadialProgressIndicatorClass = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
'progress': React.PropTypes.number.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
getPathDefinition: function(aRadius, aProgress) {
|
||||
aProgress = (aProgress <= 1) ? aProgress : 1;
|
||||
aProgress = (aProgress >= 0) ? aProgress : 0;
|
||||
|
||||
var pi = Math.PI;
|
||||
var radiantAngle = 2 * pi * aProgress;
|
||||
|
||||
var x = Math.sin( radiantAngle ) * aRadius;
|
||||
var y = Math.cos( radiantAngle ) * - aRadius;
|
||||
var mid = (aProgress > 0.5) ? 1 : 0;
|
||||
|
||||
return 'M 0 0 ' + // Start from origin
|
||||
'v ' + (-aRadius) +' ' + // Straight vertical up
|
||||
'A ' + aRadius + ' ' + aRadius + ' 1 ' + // Arc, vertical radius, horizontal radius, xAxisRotate (?)...
|
||||
mid + ' 1 ' + // ...lrge arc flag,
|
||||
x + ' ' + // ...destination x
|
||||
y + ' z'; // ...destination y, close path
|
||||
|
||||
//[x,y].forEach(function( d ){
|
||||
// d = Math.round( d * 1e3 ) / 1e3;
|
||||
//});
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
getAdditionalClassesString(aList) {
|
||||
var result;
|
||||
|
||||
aList = aList || [];
|
||||
result = aList.join(' ');
|
||||
|
||||
return ' '+result;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var border;
|
||||
|
||||
var additionalClasses = this.getAdditionalClassesString(this.props['additionalClasses']);
|
||||
|
||||
var radius = Clipperz.PM.UI.Components.DEFAULT_RADIUS;
|
||||
var borderSize = (this.props['border']) ? this.props['border'] : 0;
|
||||
var boxSize = 2 * (radius + borderSize) + 2;
|
||||
var center = boxSize/2;
|
||||
|
||||
border = null;
|
||||
if (this.props['border']) {
|
||||
border = React.DOM.circle({
|
||||
'className': 'border',
|
||||
'cx': center,
|
||||
'cy': center,
|
||||
'r': radius+this.props['border'],
|
||||
});
|
||||
}
|
||||
|
||||
return React.DOM.svg({
|
||||
'className': 'radialProgressIndicator' + additionalClasses,
|
||||
'viewBox': '0 0 ' + boxSize + ' ' + boxSize,
|
||||
}, [
|
||||
border,
|
||||
React.DOM.circle({
|
||||
'className': 'background',
|
||||
'cx': center,
|
||||
'cy': center,
|
||||
'r': radius,
|
||||
}),
|
||||
React.DOM.path({
|
||||
'className': 'progress',
|
||||
'transform': 'translate(' + (center) + ', ' + (center) + ')',
|
||||
'd': this.getPathDefinition(radius, this.props['progress'])
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
});
|
||||
|
||||
Clipperz.PM.UI.Components.RadialProgressIndicator = React.createFactory(Clipperz.PM.UI.Components.RadialProgressIndicatorClass);
|
||||
|
||||
Clipperz.PM.UI.Components.DEFAULT_RADIUS = 10; // You can resize the SVG object with CSS anyway
|
||||
@@ -36,6 +36,10 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectRecentCards');
|
||||
},
|
||||
|
||||
selectWithAttachments: function (anEvent) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCardsWithAttachments');
|
||||
},
|
||||
|
||||
selectUntaggedCards: function (anEvent) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectUntaggedCards');
|
||||
},
|
||||
@@ -76,6 +80,7 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
|
||||
var filterValue;
|
||||
|
||||
//console.log("SELECTIONS PROPS", this.props);
|
||||
//console.log("withAttachmentCardsCount", this.props['withAttachmentCardsCount']);
|
||||
tagInfo = this.props['tags'] ? this.props['tags'] : {};
|
||||
tags = MochiKit.Base.filter(Clipperz.PM.DataModel.Record.isRegularTag, MochiKit.Base.keys(tagInfo)).sort(Clipperz.Base.caseInsensitiveCompare);
|
||||
archivedCardsCount = this.props['archivedCardsCount'];
|
||||
@@ -94,7 +99,10 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
|
||||
React.DOM.span({'className':'label'}, "Recent"),
|
||||
React.DOM.span({'className':'count'}, this.props['allCardsCount'] ? '10' : '-')
|
||||
]),
|
||||
// React.DOM.li({'className':'untaggedCards', 'onClick': this.selectUntaggedCards}, "Untagged - " + this.props['untaggedCardsCount'])
|
||||
React.DOM.li({'className':'withAttachmentCards', 'onClick': this.selectWithAttachments}, [
|
||||
React.DOM.span({'className':'label'}, "With attachments"),
|
||||
React.DOM.span({'className':'count'}, this.props['withAttachmentCardsCount'] ? this.props['withAttachmentCardsCount'] : '-')
|
||||
]),
|
||||
React.DOM.li({'className':'untaggedCards', 'onClick': this.selectUntaggedCards}, [
|
||||
React.DOM.span({'className':'label'}, "Untagged"),
|
||||
React.DOM.span({'className':'count'}, this.props['untaggedCardsCount'] ? this.props['untaggedCardsCount'] : '-')
|
||||
|
||||
@@ -35,6 +35,7 @@ Clipperz.PM.UI.MainController = function() {
|
||||
|
||||
this._isSelectionPanelOpen = false;
|
||||
this._isSettingsPanelOpen = false;
|
||||
this._isAttachmentQueueBoxOpen = false;
|
||||
|
||||
this._pageStack = ['loadingPage'];
|
||||
this._overlay = new Clipperz.PM.UI.Components.Overlay();
|
||||
@@ -75,9 +76,9 @@ Clipperz.PM.UI.MainController = function() {
|
||||
'importCards',
|
||||
'downloadExport',
|
||||
'updateProgress',
|
||||
'toggleSelectionPanel', 'hideSelectionPanel', 'toggleSettingsPanel',
|
||||
'toggleSelectionPanel', 'hideSelectionPanel', 'toggleSettingsPanel', 'toggleAttachmentQueueBox',
|
||||
'matchMediaQuery', 'unmatchMediaQuery',
|
||||
'selectAllCards', 'selectRecentCards', 'search', 'tagSelected', 'selectUntaggedCards',
|
||||
'selectAllCards', 'selectRecentCards', 'selectCardsWithAttachments', 'selectUntaggedCards', 'tagSelected', 'search',
|
||||
'refreshCardEditDetail',
|
||||
'saveCardEdits', 'cancelCardEdits',
|
||||
'selectCard',
|
||||
@@ -90,9 +91,16 @@ Clipperz.PM.UI.MainController = function() {
|
||||
'closeHelp',
|
||||
'downloadOfflineCopy',
|
||||
'runDirectLogin', 'removeDirectLogin',
|
||||
'exitSearch'
|
||||
'exitSearch',
|
||||
'updateAttachmentQueueInfo', 'addAttachment', 'removeAttachment', 'getAttachment', 'cancelAttachment', 'closeAttachmentNotification',
|
||||
]);
|
||||
|
||||
this._attachmentController = new Clipperz.PM.UI.AttachmentController({
|
||||
'uploadMessageCallback': MochiKit.Base.method(this, 'uploadMessageCallback'),
|
||||
'downloadMessageCallback': MochiKit.Base.method(this, 'downloadMessageCallback'),
|
||||
'reloadServerStatusCallback': MochiKit.Base.method(this, 'reloadAttachmentServerStatusCallback')
|
||||
});
|
||||
|
||||
Mousetrap.bind(['/'], MochiKit.Base.method(this, 'focusOnSearch'));
|
||||
|
||||
Mousetrap.bind(['left', 'h', 'esc'], MochiKit.Base.method(this, 'exitCurrentSelection'));
|
||||
@@ -205,7 +213,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
renderPages: function (pages) {
|
||||
var self = this;
|
||||
MochiKit.Iter.forEach(pages, function (aPageName) {
|
||||
//console.log("RENDERING", aPageName);
|
||||
self._pages[aPageName] = React.render(
|
||||
Clipperz.PM.UI.Components.Pages[self.capitaliseFirstLetter(aPageName)](self.pageProperties(aPageName)),
|
||||
MochiKit.DOM.getElement(aPageName)
|
||||
@@ -437,7 +444,7 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, passphrase);
|
||||
user = new Clipperz.PM.DataModel.User({'username':oldUser.username(), 'getPassphraseFunction':getPassphraseDelegate});
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.unlock_handler', {trace:true});
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.unlock_handler', {trace:false});
|
||||
|
||||
deferredResult.addMethod(unlockPage, 'setProps', {'disabled': true});
|
||||
|
||||
@@ -539,7 +546,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
},
|
||||
|
||||
setUser: function (aUser) {
|
||||
//console.log("SET USER", aUser);
|
||||
this._user = aUser;
|
||||
return this._user;
|
||||
},
|
||||
@@ -604,11 +610,30 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
collectAttachmentInfo: function(anAttachment) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.collectAttachmentInfo', {trace:false});
|
||||
deferredResult.setValue('_attachment'); // The object itself, maybe this should be the only value being passed
|
||||
deferredResult.addMethod(anAttachment, 'reference');
|
||||
deferredResult.setValue('_reference');
|
||||
deferredResult.addMethod(anAttachment, 'name');
|
||||
deferredResult.setValue('name');
|
||||
deferredResult.addMethod(anAttachment, 'contentType');
|
||||
deferredResult.setValue('contentType');
|
||||
deferredResult.addMethod(anAttachment, 'size');
|
||||
deferredResult.setValue('size');
|
||||
deferredResult.values();
|
||||
|
||||
deferredResult.callback(anAttachment);
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
collectRecordInfo: function (aRecord) {
|
||||
var deferredResult;
|
||||
|
||||
//console.log("collectRecordInfo");
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.collectRecordInfo', {trace:false});
|
||||
deferredResult.setValue('_record');
|
||||
deferredResult.addMethod(aRecord, 'reference');
|
||||
@@ -631,12 +656,21 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.setValue('fields');
|
||||
|
||||
deferredResult.addMethod(aRecord, 'attachments');
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectAttachmentInfo'));
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.setValue('attachments');
|
||||
|
||||
deferredResult.addMethod(aRecord, 'directLogins');
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectDirectLoginInfo'));
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.setValue('directLogins');
|
||||
|
||||
deferredResult.addMethod(aRecord, 'getAttachmentServerStatus');
|
||||
deferredResult.setValue('attachmentServerStatus');
|
||||
|
||||
deferredResult.values();
|
||||
|
||||
deferredResult.callback(aRecord);
|
||||
@@ -668,8 +702,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.updateSelectedCard', {trace:false});
|
||||
deferredResult.addMethod(this.user(), 'getRecord', someInfo['reference']);
|
||||
|
||||
// deferredResult.addMethod(this, function(d) {console.log(d); return d;});
|
||||
|
||||
deferredResult.addMethod(this, 'collectRecordInfo');
|
||||
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'selectedCard');
|
||||
if ((this.mediaQueryStyle() == 'narrow') && shouldShowCardDetail) {
|
||||
@@ -774,6 +806,10 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
filterCriteria = MochiKit.Base.operator.truth;
|
||||
sortCriteria = Clipperz.Base.reverseComparator(MochiKit.Base.keyComparator('_accessDate'));
|
||||
rangeFilter = function (someCards) { return someCards.slice(0, 10)};
|
||||
} else if (aFilter['type'] == 'WITH_ATTACHMENTS') {
|
||||
filterCriteria = function (aRecordInfo) { return aRecordInfo.attachmentsCount > 0; };
|
||||
sortCriteria = Clipperz.Base.caseInsensitiveKeyComparator('label');
|
||||
rangeFilter = MochiKit.Base.operator.identity;
|
||||
} else if (aFilter['type'] == 'SEARCH') {
|
||||
filterCriteria = this.regExpFilterGenerator(Clipperz.PM.DataModel.Record.regExpForSearch(aFilter['value']));
|
||||
sortCriteria = Clipperz.Base.caseInsensitiveKeyComparator('label');
|
||||
@@ -879,7 +915,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
MochiKit.Base.method(this.user(), 'getPreference', 'lock'),
|
||||
MochiKit.Base.bind(function (someLockInfo) {
|
||||
if (this._lockTimeout) {
|
||||
// console.log("clearing previous lock timer");
|
||||
clearTimeout(this._lockTimeout);
|
||||
}
|
||||
|
||||
@@ -902,7 +937,7 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
? MochiKit.Async.succeed
|
||||
: MochiKit.Base.partial(MochiKit.Base.filter, function (someRecordInfo) { return ! someRecordInfo['_isArchived']; });
|
||||
|
||||
return Clipperz.Async.callbacks("MainController.getUntaggedCardsCount", [
|
||||
return Clipperz.Async.callbacks("MainController.getAllCardsCount", [
|
||||
MochiKit.Base.method(this.user(), 'getRecords'),
|
||||
MochiKit.Base.partial(MochiKit.Base.map, Clipperz.Async.collectResults("collectResults", {'_fullLabel':MochiKit.Base.methodcaller('fullLabel'), '_isArchived':MochiKit.Base.methodcaller('isArchived')}, {trace:false})),
|
||||
Clipperz.Async.collectAll,
|
||||
@@ -910,7 +945,23 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
function (someCards) { return someCards.length; },
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
|
||||
getCardsWithAttachmentsCount: function () {
|
||||
var archivedCardsFilter = this.shouldIncludeArchivedCards()
|
||||
? MochiKit.Async.succeed
|
||||
: MochiKit.Base.partial(MochiKit.Base.filter, function (someRecordInfo) { return ! someRecordInfo['_isArchived']; });
|
||||
|
||||
return Clipperz.Async.callbacks("MainController.getCardsWithAttachmentsCount", [
|
||||
MochiKit.Base.method(this.user(), 'getRecords'),
|
||||
MochiKit.Base.partial(MochiKit.Base.map, Clipperz.Async.collectResults("collectResults", {'attachmentsCount':MochiKit.Base.methodcaller('attachmentsCount'), '_isArchived':MochiKit.Base.methodcaller('isArchived')}, {trace:false})),
|
||||
Clipperz.Async.collectAll,
|
||||
archivedCardsFilter,
|
||||
function (aResult) {
|
||||
return MochiKit.Base.filter(function (aRecordInfo) { return aRecordInfo['attachmentsCount'] > 0; }, aResult).length;
|
||||
}
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
getArchivedCardsCount: function () {
|
||||
return Clipperz.Async.callbacks("MainController.getArchivedCardsCount", [
|
||||
MochiKit.Base.method(this.user(), 'getRecords'),
|
||||
@@ -961,6 +1012,8 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
MochiKit.Base.method(this, 'setPageProperties', 'mainPage', 'allTags'),
|
||||
MochiKit.Base.method(this, 'getAllCardsCount'),
|
||||
MochiKit.Base.method(this, 'setPageProperties', 'mainPage', 'allCardsCount'),
|
||||
MochiKit.Base.method(this, 'getCardsWithAttachmentsCount'),
|
||||
MochiKit.Base.method(this, 'setPageProperties', 'mainPage', 'withAttachmentCardsCount'),
|
||||
MochiKit.Base.method(this, 'getArchivedCardsCount'),
|
||||
MochiKit.Base.method(this, 'setPageProperties', 'mainPage', 'archivedCardsCount'),
|
||||
MochiKit.Base.method(this, 'getUntaggedCardsCount'),
|
||||
@@ -979,9 +1032,12 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
//=========================================================================
|
||||
|
||||
runApplication: function (anUser) {
|
||||
window.history.replaceState({selectedCardInfo: null}, "", window.location.toString());
|
||||
window.history.pushState({selectedCardInfo: null}, "", window.location.toString()); // Hack to support filters undo with no other actions
|
||||
this.moveInPage(this.currentPage(), 'mainPage');
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'enableLock');
|
||||
this.resetLockTimeout();
|
||||
this.registerHistoryHandler();
|
||||
|
||||
return this.renderAccountData();
|
||||
},
|
||||
@@ -1129,11 +1185,13 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'LEFT');
|
||||
this.setCurrentPage(toPage);
|
||||
|
||||
if (shouldAddItemToHistory) {
|
||||
window.history.pushState({'fromPage': fromPage, 'toPage': toPage}, "");
|
||||
} else {
|
||||
// Skipping push on page change: history needs to be more granular
|
||||
// New states are pushed on user actions (view card, back to main page, ...)
|
||||
// if (shouldAddItemToHistory) {
|
||||
// window.history.pushState({'fromPage': fromPage, 'toPage': toPage}, "");
|
||||
// } else {
|
||||
//console.log("Skip HISTORY");
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
//console.log("No need to move in the same page");
|
||||
}
|
||||
@@ -1202,6 +1260,32 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
}
|
||||
},
|
||||
|
||||
attachmentController: function () {
|
||||
return this._attachmentController;
|
||||
},
|
||||
|
||||
attachmentQueueInfo: function() {
|
||||
var queue;
|
||||
var notifications;
|
||||
var elementFetchCallback;
|
||||
|
||||
if (this._attachmentController) {
|
||||
queue = this._attachmentController.getQueueInfo();
|
||||
notifications = this._attachmentController.getNotificationsInfo();
|
||||
elementFetchCallback = MochiKit.Base.method(this._attachmentController, 'getQueueElement');
|
||||
} else {
|
||||
queue = [];
|
||||
notifications = [];
|
||||
elementFetchCallback = null;
|
||||
}
|
||||
|
||||
return {
|
||||
'queue': queue,
|
||||
'notifications': notifications,
|
||||
'elementFetchCallback': elementFetchCallback,
|
||||
}
|
||||
},
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
messageBoxContent: function () {
|
||||
@@ -1211,7 +1295,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
message = "";
|
||||
level = 'HIDE';
|
||||
|
||||
//console.log("messageBox - this.user()", this.user());
|
||||
if (this.featureSet() == 'EXPIRED') {
|
||||
message = "Expired subscription";
|
||||
level = 'ERROR';
|
||||
@@ -1261,12 +1344,14 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
'messageBox': this.messageBoxContent(),
|
||||
'userInfo': this.userInfo(),
|
||||
'accountInfo': this.userAccountInfo(),
|
||||
'selectionPanelStatus': this.isSelectionPanelOpen() ? 'OPEN' : 'CLOSED',
|
||||
'settingsPanelStatus': this.isSettingsPanelOpen() ? 'OPEN' : 'CLOSED',
|
||||
'selectionPanelStatus': this.isSelectionPanelOpen() ? 'OPEN' : 'CLOSED',
|
||||
'settingsPanelStatus': this.isSettingsPanelOpen() ? 'OPEN' : 'CLOSED',
|
||||
'attachmentQueueBoxStatus': this.isAttachmentQueueBoxOpen() ? 'OPEN' : 'CLOSED',
|
||||
'featureSet': this.featureSet(),
|
||||
'features': this.features(),
|
||||
'proxyInfo': this.proxyInfo(),
|
||||
'locked': false
|
||||
'locked': false,
|
||||
'attachmentQueueInfo': this.attachmentQueueInfo(),
|
||||
// 'shouldIncludeArchivedCards': this.shouldIncludeArchivedCards(),
|
||||
// 'cards': …,
|
||||
// 'tags': …,
|
||||
@@ -1274,6 +1359,7 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
};
|
||||
} else if (aPageName == 'cardDetailPage') {
|
||||
extraProperties = {
|
||||
'attachmentQueueInfo': this.attachmentQueueInfo(),
|
||||
};
|
||||
} else if (aPageName == 'errorPage') {
|
||||
extraProperties = {
|
||||
@@ -1367,6 +1453,15 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
this.refreshCurrentPage();
|
||||
},
|
||||
|
||||
isAttachmentQueueBoxOpen: function() {
|
||||
return this._isAttachmentQueueBoxOpen;
|
||||
},
|
||||
|
||||
toggleAttachmentQueueBox_handler: function (anEvent) {
|
||||
this._isAttachmentQueueBoxOpen = !this._isAttachmentQueueBoxOpen;
|
||||
this.refreshCurrentPage();
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
selectedCardInfo: function () {
|
||||
@@ -1487,6 +1582,8 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
selectCard_handler: function (someInfo, shouldUpdateCardDetail) {
|
||||
this.selectCard(someInfo, shouldUpdateCardDetail);
|
||||
window.history.pushState({selectedCardInfo: someInfo}, "", window.location.toString());
|
||||
// console.log("pushing state");
|
||||
},
|
||||
|
||||
refreshCardEditDetail_handler: function (aRecordReference) {
|
||||
@@ -1689,6 +1786,10 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
},
|
||||
|
||||
cancelCardEdits_handler: function (aRecordReference) {
|
||||
return this.cancelCardEdits(aRecordReference);
|
||||
},
|
||||
|
||||
cancelCardEdits: function (aRecordReference) {
|
||||
var currentPage = this.pages()[this.currentPage()];
|
||||
var self = this;
|
||||
var wasBrandNew;
|
||||
@@ -1699,7 +1800,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
function (aValue) { wasBrandNew = aValue },
|
||||
|
||||
MochiKit.Base.method(this.user(), 'hasPendingChanges'),
|
||||
//function (aValue) { console.log("2- USER.hasPendingChanges()", aValue); return aValue; },
|
||||
Clipperz.Async.deferredIf('HasPendingChanges',[
|
||||
MochiKit.Base.method(self, 'ask', {
|
||||
'question': "There are pending changes to your card. Ignore changes?",
|
||||
@@ -1808,7 +1908,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
cloneCard_handler: function (anEvent) {
|
||||
var cardInfo;
|
||||
|
||||
//console.log("CLONE CARD", anEvent['reference']);
|
||||
return Clipperz.Async.callbacks("MainController.cloneCard_handler", [
|
||||
MochiKit.Base.method(this.user(), 'getRecord', anEvent['reference']),
|
||||
MochiKit.Base.method(this.user(), 'cloneRecord'),
|
||||
@@ -1827,6 +1926,11 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
MochiKit.Base.method(this, 'refreshUI'),
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
isPageInEditMode: function() {
|
||||
var currentPage = this.pages()[this.currentPage()];
|
||||
return currentPage.props['mode'] == 'edit';
|
||||
},
|
||||
|
||||
enterEditMode: function () {
|
||||
var currentPage = this.pages()[this.currentPage()];
|
||||
@@ -1844,8 +1948,6 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
},
|
||||
|
||||
editCard_handler: function (anEvent) {
|
||||
//console.log("EDIT CARD", anEvent['reference']);
|
||||
// this.pages()[this.currentPage()].setProps({'mode': 'edit'});
|
||||
this.enterEditMode();
|
||||
},
|
||||
|
||||
@@ -1881,11 +1983,16 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
goBackToMainPage_handler: function (anEvent) {
|
||||
this.goBackToMainPage(anEvent);
|
||||
window.history.pushState({selectedCardInfo: null}, "", window.location.toString());
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
selectAllCards_handler: function () {
|
||||
this.selectAllCards();
|
||||
},
|
||||
|
||||
selectAllCards: function () {
|
||||
this.setPageProperties('mainPage', 'searchTerm', '');
|
||||
this.resetSelectedCard();
|
||||
this.setFilter('ALL');
|
||||
@@ -1900,6 +2007,13 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
return this.refreshSelectedCards();
|
||||
},
|
||||
|
||||
selectCardsWithAttachments_handler: function () {
|
||||
this.resetSelectedCard();
|
||||
this.setFilter('WITH_ATTACHMENTS');
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'hideSelectionPanel');
|
||||
return this.refreshSelectedCards();
|
||||
},
|
||||
|
||||
search_handler: function (aValue) {
|
||||
this.resetSelectedCard();
|
||||
|
||||
@@ -2044,7 +2158,7 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
prevCardInfo = this.previousCardInfo();
|
||||
shouldUpdateCardDetail = this.shouldShowCardDetailWhenMovingBetweenCardsUsingKeys();
|
||||
//console.log("PREV CARD INFO", prevCardInfo);
|
||||
|
||||
if (prevCardInfo != null) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', prevCardInfo, shouldUpdateCardDetail);
|
||||
}
|
||||
@@ -2056,7 +2170,7 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
nextCardInfo = this.nextCardInfo();
|
||||
shouldUpdateCardDetail = this.shouldShowCardDetailWhenMovingBetweenCardsUsingKeys();
|
||||
//console.log("NEXT CARD INFO", shouldUpdateCardDetail);
|
||||
|
||||
if (nextCardInfo != null) {
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', nextCardInfo, shouldUpdateCardDetail);
|
||||
}
|
||||
@@ -2086,6 +2200,128 @@ Clipperz.log("THE BROWSER IS OFFLINE");
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
updateAttachmentQueueInfo_handler: function(someProperties) {
|
||||
this.setPageProperties(this.currentPage(), 'attachmentQueueInfo', this.attachmentQueueInfo());
|
||||
},
|
||||
|
||||
addAttachment_handler: function(aRecord, aFile) { // aReference, someMetadata, aKey, aNonce
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("MainController.addCardAttachment_handler", {'trace':false});
|
||||
|
||||
deferredResult.addMethod(aRecord, 'createNewAttachment');
|
||||
deferredResult.addCallback(MochiKit.Base.methodcaller('setFile', aFile));
|
||||
deferredResult.addMethod(this.attachmentController(), 'addAttachment');
|
||||
deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'refreshCardEditDetail', aRecord.reference());
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
uploadMessageCallback: function(someArguments, aProgressCallback) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("MainController.uploadMessageCallback", {'trace':false});
|
||||
deferredResult.addMethod(this.user().connection(), 'uploadAttachment', someArguments, aProgressCallback);
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
removeAttachment_handler: function(aRecord, anAttachment) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("MainController.removeAttachment_handler", {trace: false});
|
||||
deferredResult.addCallback(MochiKit.Base.method(this, 'ask', {
|
||||
'question': "Do you really want to delete this attachment?",
|
||||
'possibleAnswers':{
|
||||
'cancel': {'label':"No", 'isDefault':true, 'answer':MochiKit.Base.methodcaller('cancel', new MochiKit.Async.CancelledError())},
|
||||
'revert': {'label':"Yes", 'isDefault':false, 'answer':MochiKit.Base.methodcaller('callback')}
|
||||
}
|
||||
})),
|
||||
deferredResult.addMethod(this, 'cancelAttachment_handler', anAttachment);
|
||||
deferredResult.addMethod(aRecord, 'removeAttachment', anAttachment);
|
||||
deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'refreshCardEditDetail', aRecord.reference());
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
getAttachment_handler: function(anAttachment) {
|
||||
this.attachmentController().getAttachment(anAttachment);
|
||||
},
|
||||
|
||||
downloadMessageCallback: function(anAttachment, aProgressCallback) {
|
||||
var deferredResult;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("MainController.downloadAttachment_handler", {'trace':false});
|
||||
deferredResult.addMethod(this.user().connection(), 'downloadAttachment', {
|
||||
'reference': anAttachment.reference()
|
||||
}, aProgressCallback);
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
cancelAttachment_handler: function(anAttachment) {
|
||||
return this.attachmentController().cancelAttachment(anAttachment);
|
||||
},
|
||||
|
||||
closeAttachmentNotification_handler: function(aNotificationId) {
|
||||
return this.attachmentController().removeNotification(aNotificationId);
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
reloadAttachmentServerStatusCallback: function(aRecord) {
|
||||
return Clipperz.Async.callbacks("MainController.reloadAttachmentServerStatus_handler", [
|
||||
MochiKit.Base.method(this.user(), 'getRecordDetail', aRecord),
|
||||
MochiKit.Base.bind(function () {
|
||||
if (this._selectedCardInfo && this._selectedCardInfo['reference']) {
|
||||
return this.refreshUI(this._selectedCardInfo['reference']);
|
||||
}
|
||||
}, this),
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
registerHistoryHandler: function() {
|
||||
window.onpopstate = MochiKit.Base.method(this, 'handleOnpopstate');
|
||||
},
|
||||
|
||||
handleOnpopstate: function (anEvent) {
|
||||
if (this.filter().type != 'ALL') {
|
||||
this.selectAllCards();
|
||||
window.history.pushState(window.history.state, "", window.location.toString());
|
||||
} else if(anEvent.state) {
|
||||
if (this.isPageInEditMode()) {
|
||||
window.history.pushState({selectedCardInfo: this.selectedCardInfo()}, "", window.location.toString());
|
||||
this.cancelCardEdits(this.selectedCardReference());
|
||||
} else {
|
||||
if (anEvent.state['selectedCardInfo']) {
|
||||
this.selectCard(anEvent.state['selectedCardInfo'], true);
|
||||
} else {
|
||||
this.selectCard(null, true);
|
||||
this.goBackToMainPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log('History changed', anEvent.state);
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
/*
|
||||
wrongAppVersion: function (anError) {
|
||||
// this.pages()['errorPage'].setProps({message:anError.message});
|
||||
|
||||
30
frontend/delta/js/Clipperz/Sound.js
Normal file
30
frontend/delta/js/Clipperz/Sound.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
Clipperz.Base.module('Clipperz.Sound');
|
||||
|
||||
Clipperz.Sound.beep = function() {
|
||||
var snd = new Audio("data:audio/wav;base64,//uQRAAAAWMSLwUIYAAsYkXgoQwAEaYLWfkWgAI0wWs/ItAAAGDgYtAgAyN+QWaAAihwMWm4G8QQRDiMcCBcH3Cc+CDv/7xA4Tvh9Rz/y8QADBwMWgQAZG/ILNAARQ4GLTcDeIIIhxGOBAuD7hOfBB3/94gcJ3w+o5/5eIAIAAAVwWgQAVQ2ORaIQwEMAJiDg95G4nQL7mQVWI6GwRcfsZAcsKkJvxgxEjzFUgfHoSQ9Qq7KNwqHwuB13MA4a1q/DmBrHgPcmjiGoh//EwC5nGPEmS4RcfkVKOhJf+WOgoxJclFz3kgn//dBA+ya1GhurNn8zb//9NNutNuhz31f////9vt///z+IdAEAAAK4LQIAKobHItEIYCGAExBwe8jcToF9zIKrEdDYIuP2MgOWFSE34wYiR5iqQPj0JIeoVdlG4VD4XA67mAcNa1fhzA1jwHuTRxDUQ//iYBczjHiTJcIuPyKlHQkv/LHQUYkuSi57yQT//uggfZNajQ3Vmz+Zt//+mm3Wm3Q576v////+32///5/EOgAAADVghQAAAAA//uQZAUAB1WI0PZugAAAAAoQwAAAEk3nRd2qAAAAACiDgAAAAAAABCqEEQRLCgwpBGMlJkIz8jKhGvj4k6jzRnqasNKIeoh5gI7BJaC1A1AoNBjJgbyApVS4IDlZgDU5WUAxEKDNmmALHzZp0Fkz1FMTmGFl1FMEyodIavcCAUHDWrKAIA4aa2oCgILEBupZgHvAhEBcZ6joQBxS76AgccrFlczBvKLC0QI2cBoCFvfTDAo7eoOQInqDPBtvrDEZBNYN5xwNwxQRfw8ZQ5wQVLvO8OYU+mHvFLlDh05Mdg7BT6YrRPpCBznMB2r//xKJjyyOh+cImr2/4doscwD6neZjuZR4AgAABYAAAABy1xcdQtxYBYYZdifkUDgzzXaXn98Z0oi9ILU5mBjFANmRwlVJ3/6jYDAmxaiDG3/6xjQQCCKkRb/6kg/wW+kSJ5//rLobkLSiKmqP/0ikJuDaSaSf/6JiLYLEYnW/+kXg1WRVJL/9EmQ1YZIsv/6Qzwy5qk7/+tEU0nkls3/zIUMPKNX/6yZLf+kFgAfgGyLFAUwY//uQZAUABcd5UiNPVXAAAApAAAAAE0VZQKw9ISAAACgAAAAAVQIygIElVrFkBS+Jhi+EAuu+lKAkYUEIsmEAEoMeDmCETMvfSHTGkF5RWH7kz/ESHWPAq/kcCRhqBtMdokPdM7vil7RG98A2sc7zO6ZvTdM7pmOUAZTnJW+NXxqmd41dqJ6mLTXxrPpnV8avaIf5SvL7pndPvPpndJR9Kuu8fePvuiuhorgWjp7Mf/PRjxcFCPDkW31srioCExivv9lcwKEaHsf/7ow2Fl1T/9RkXgEhYElAoCLFtMArxwivDJJ+bR1HTKJdlEoTELCIqgEwVGSQ+hIm0NbK8WXcTEI0UPoa2NbG4y2K00JEWbZavJXkYaqo9CRHS55FcZTjKEk3NKoCYUnSQ0rWxrZbFKbKIhOKPZe1cJKzZSaQrIyULHDZmV5K4xySsDRKWOruanGtjLJXFEmwaIbDLX0hIPBUQPVFVkQkDoUNfSoDgQGKPekoxeGzA4DUvnn4bxzcZrtJyipKfPNy5w+9lnXwgqsiyHNeSVpemw4bWb9psYeq//uQZBoABQt4yMVxYAIAAAkQoAAAHvYpL5m6AAgAACXDAAAAD59jblTirQe9upFsmZbpMudy7Lz1X1DYsxOOSWpfPqNX2WqktK0DMvuGwlbNj44TleLPQ+Gsfb+GOWOKJoIrWb3cIMeeON6lz2umTqMXV8Mj30yWPpjoSa9ujK8SyeJP5y5mOW1D6hvLepeveEAEDo0mgCRClOEgANv3B9a6fikgUSu/DmAMATrGx7nng5p5iimPNZsfQLYB2sDLIkzRKZOHGAaUyDcpFBSLG9MCQALgAIgQs2YunOszLSAyQYPVC2YdGGeHD2dTdJk1pAHGAWDjnkcLKFymS3RQZTInzySoBwMG0QueC3gMsCEYxUqlrcxK6k1LQQcsmyYeQPdC2YfuGPASCBkcVMQQqpVJshui1tkXQJQV0OXGAZMXSOEEBRirXbVRQW7ugq7IM7rPWSZyDlM3IuNEkxzCOJ0ny2ThNkyRai1b6ev//3dzNGzNb//4uAvHT5sURcZCFcuKLhOFs8mLAAEAt4UWAAIABAAAAAB4qbHo0tIjVkUU//uQZAwABfSFz3ZqQAAAAAngwAAAE1HjMp2qAAAAACZDgAAAD5UkTE1UgZEUExqYynN1qZvqIOREEFmBcJQkwdxiFtw0qEOkGYfRDifBui9MQg4QAHAqWtAWHoCxu1Yf4VfWLPIM2mHDFsbQEVGwyqQoQcwnfHeIkNt9YnkiaS1oizycqJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
|
||||
snd.play();
|
||||
};
|
||||
171
frontend/delta/js/Filesize/filesize.es6.js
Normal file
171
frontend/delta/js/Filesize/filesize.es6.js
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* filesize
|
||||
*
|
||||
* @author Jason Mulligan <jason.mulligan@avoidwork.com>
|
||||
* @copyright 2015 Jason Mulligan <jason.mulligan@avoidwork.com>
|
||||
* @license BSD-3-Clause
|
||||
* @link http://filesizejs.com
|
||||
* @module filesize
|
||||
* @version 3.1.3
|
||||
*/
|
||||
( global ) => {
|
||||
const bit = /b$/;
|
||||
const si = {
|
||||
bits: [ "B", "kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb" ],
|
||||
bytes: [ "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]
|
||||
};
|
||||
|
||||
/**
|
||||
* filesize
|
||||
*
|
||||
* @method filesize
|
||||
* @param {Mixed} arg String, Int or Float to transform
|
||||
* @param {Object} descriptor [Optional] Flags
|
||||
* @return {String} Readable file size String
|
||||
*/
|
||||
let filesize = ( arg, descriptor={} ) => {
|
||||
let result = [];
|
||||
let skip = false;
|
||||
let val = 0;
|
||||
let e, base, bits, ceil, neg, num, output, round, unix, spacer, suffixes;
|
||||
|
||||
if ( isNaN( arg ) ) {
|
||||
throw new Error( "Invalid arguments" );
|
||||
}
|
||||
|
||||
bits = ( descriptor.bits === true );
|
||||
unix = ( descriptor.unix === true );
|
||||
base = descriptor.base !== undefined ? descriptor.base : 2;
|
||||
round = descriptor.round !== undefined ? descriptor.round : unix ? 1 : 2;
|
||||
spacer = descriptor.spacer !== undefined ? descriptor.spacer : unix ? "" : " ";
|
||||
suffixes = descriptor.suffixes !== undefined ? descriptor.suffixes : {};
|
||||
output = descriptor.output !== undefined ? descriptor.output : "string";
|
||||
e = descriptor.exponent !== undefined ? descriptor.exponent : -1;
|
||||
num = Number( arg );
|
||||
neg = ( num < 0 );
|
||||
ceil = base > 2 ? 1000 : 1024;
|
||||
|
||||
// Flipping a negative number to determine the size
|
||||
if ( neg ) {
|
||||
num = -num;
|
||||
}
|
||||
|
||||
// Zero is now a special case because bytes divide by 1
|
||||
if ( num === 0 ) {
|
||||
result[ 0 ] = 0;
|
||||
|
||||
if ( unix ) {
|
||||
result[ 1 ] = "";
|
||||
}
|
||||
else {
|
||||
result[ 1 ] = "B";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Determining the exponent
|
||||
if ( e === -1 || isNaN( e ) ) {
|
||||
e = Math.floor( Math.log( num ) / Math.log( ceil ) );
|
||||
}
|
||||
|
||||
// Exceeding supported length, time to reduce & multiply
|
||||
if ( e > 8 ) {
|
||||
val = val * ( 1000 * ( e - 8 ) );
|
||||
e = 8;
|
||||
}
|
||||
|
||||
if ( base === 2 ) {
|
||||
val = num / Math.pow( 2, ( e * 10 ) );
|
||||
}
|
||||
else {
|
||||
val = num / Math.pow( 1000, e );
|
||||
}
|
||||
|
||||
if ( bits ) {
|
||||
val = ( val * 8 );
|
||||
|
||||
if ( val > ceil ) {
|
||||
val = val / ceil;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
|
||||
result[ 0 ] = Number( val.toFixed( e > 0 ? round : 0 ) );
|
||||
result[ 1 ] = si[ bits ? "bits" : "bytes" ][ e ];
|
||||
|
||||
if ( !skip && unix ) {
|
||||
if ( bits && bit.test( result[ 1 ] ) ) {
|
||||
result[ 1 ] = result[ 1 ].toLowerCase();
|
||||
}
|
||||
|
||||
result[ 1 ] = result[ 1 ].charAt( 0 );
|
||||
|
||||
if ( result[ 1 ] === "B" ) {
|
||||
result[ 0 ] = Math.floor( result[ 0 ] );
|
||||
result[ 1 ] = "";
|
||||
}
|
||||
else if ( !bits && result[ 1 ] === "k" ) {
|
||||
result[ 1 ] = "K";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decorating a 'diff'
|
||||
if ( neg ) {
|
||||
result[ 0 ] = -result[ 0 ];
|
||||
}
|
||||
|
||||
// Applying custom suffix
|
||||
result[ 1 ] = suffixes[ result[ 1 ] ] || result[ 1 ];
|
||||
|
||||
// Returning Array, Object, or String (default)
|
||||
if ( output === "array" ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( output === "exponent" ) {
|
||||
return e;
|
||||
}
|
||||
|
||||
if ( output === "object" ) {
|
||||
return { value: result[ 0 ], suffix: result[ 1 ] };
|
||||
}
|
||||
|
||||
return result.join( spacer );
|
||||
}
|
||||
|
||||
// CommonJS, AMD, script tag
|
||||
if ( typeof exports !== "undefined" ) {
|
||||
module.exports = filesize;
|
||||
}
|
||||
else if ( typeof define === "function" ) {
|
||||
define( () => {
|
||||
return filesize;
|
||||
} );
|
||||
}
|
||||
else {
|
||||
global.filesize = filesize;
|
||||
}
|
||||
}( typeof global !== "undefined" ? global : window );
|
||||
179
frontend/delta/js/Filesize/filesize.js
Normal file
179
frontend/delta/js/Filesize/filesize.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* filesize
|
||||
*
|
||||
* @author Jason Mulligan <jason.mulligan@avoidwork.com>
|
||||
* @copyright 2015 Jason Mulligan <jason.mulligan@avoidwork.com>
|
||||
* @license BSD-3-Clause
|
||||
* @link http://filesizejs.com
|
||||
* @module filesize
|
||||
* @version 3.1.3
|
||||
*/
|
||||
(function (global) {
|
||||
var bit = /b$/;
|
||||
var si = {
|
||||
bits: ["B", "kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb"],
|
||||
bytes: ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||
};
|
||||
|
||||
/**
|
||||
* filesize
|
||||
*
|
||||
* @method filesize
|
||||
* @param {Mixed} arg String, Int or Float to transform
|
||||
* @param {Object} descriptor [Optional] Flags
|
||||
* @return {String} Readable file size String
|
||||
*/
|
||||
var filesize = function (arg) {
|
||||
var descriptor = arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
var result = [];
|
||||
var skip = false;
|
||||
var val = 0;
|
||||
var e = undefined,
|
||||
base = undefined,
|
||||
bits = undefined,
|
||||
ceil = undefined,
|
||||
neg = undefined,
|
||||
num = undefined,
|
||||
output = undefined,
|
||||
round = undefined,
|
||||
unix = undefined,
|
||||
spacer = undefined,
|
||||
suffixes = undefined;
|
||||
|
||||
if (isNaN(arg)) {
|
||||
throw new Error("Invalid arguments");
|
||||
}
|
||||
|
||||
bits = descriptor.bits === true;
|
||||
unix = descriptor.unix === true;
|
||||
base = descriptor.base !== undefined ? descriptor.base : 2;
|
||||
round = descriptor.round !== undefined ? descriptor.round : unix ? 1 : 2;
|
||||
spacer = descriptor.spacer !== undefined ? descriptor.spacer : unix ? "" : " ";
|
||||
suffixes = descriptor.suffixes !== undefined ? descriptor.suffixes : {};
|
||||
output = descriptor.output !== undefined ? descriptor.output : "string";
|
||||
e = descriptor.exponent !== undefined ? descriptor.exponent : -1;
|
||||
num = Number(arg);
|
||||
neg = num < 0;
|
||||
ceil = base > 2 ? 1000 : 1024;
|
||||
|
||||
// Flipping a negative number to determine the size
|
||||
if (neg) {
|
||||
num = -num;
|
||||
}
|
||||
|
||||
// Zero is now a special case because bytes divide by 1
|
||||
if (num === 0) {
|
||||
result[0] = 0;
|
||||
|
||||
if (unix) {
|
||||
result[1] = "";
|
||||
} else {
|
||||
result[1] = "B";
|
||||
}
|
||||
} else {
|
||||
// Determining the exponent
|
||||
if (e === -1 || isNaN(e)) {
|
||||
e = Math.floor(Math.log(num) / Math.log(ceil));
|
||||
}
|
||||
|
||||
// Exceeding supported length, time to reduce & multiply
|
||||
if (e > 8) {
|
||||
val = val * (1000 * (e - 8));
|
||||
e = 8;
|
||||
}
|
||||
|
||||
if (base === 2) {
|
||||
val = num / Math.pow(2, e * 10);
|
||||
} else {
|
||||
val = num / Math.pow(1000, e);
|
||||
}
|
||||
|
||||
if (bits) {
|
||||
val = val * 8;
|
||||
|
||||
if (val > ceil) {
|
||||
val = val / ceil;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
|
||||
result[0] = Number(val.toFixed(e > 0 ? round : 0));
|
||||
result[1] = si[bits ? "bits" : "bytes"][e];
|
||||
|
||||
if (!skip && unix) {
|
||||
if (bits && bit.test(result[1])) {
|
||||
result[1] = result[1].toLowerCase();
|
||||
}
|
||||
|
||||
result[1] = result[1].charAt(0);
|
||||
|
||||
if (result[1] === "B") {
|
||||
result[0] = Math.floor(result[0]);
|
||||
result[1] = "";
|
||||
} else if (!bits && result[1] === "k") {
|
||||
result[1] = "K";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decorating a 'diff'
|
||||
if (neg) {
|
||||
result[0] = -result[0];
|
||||
}
|
||||
|
||||
// Applying custom suffix
|
||||
result[1] = suffixes[result[1]] || result[1];
|
||||
|
||||
// Returning Array, Object, or String (default)
|
||||
if (output === "array") {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (output === "exponent") {
|
||||
return e;
|
||||
}
|
||||
|
||||
if (output === "object") {
|
||||
return { value: result[0], suffix: result[1] };
|
||||
}
|
||||
|
||||
return result.join(spacer);
|
||||
};
|
||||
|
||||
// CommonJS, AMD, script tag
|
||||
if (typeof exports !== "undefined") {
|
||||
module.exports = filesize;
|
||||
} else if (typeof define === "function") {
|
||||
define(function () {
|
||||
return filesize;
|
||||
});
|
||||
} else {
|
||||
global.filesize = filesize;
|
||||
}
|
||||
})(typeof global !== "undefined" ? global : window);
|
||||
29
frontend/delta/js/Filesize/filesize.min.js
vendored
Normal file
29
frontend/delta/js/Filesize/filesize.min.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
* Clipperz is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
2015
|
||||
@version 3.1.3
|
||||
*/
|
||||
"use strict";!function(a){var b=/b$/,c={bits:["B","kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb"],bytes:["B","kB","MB","GB","TB","PB","EB","ZB","YB"]},d=function(a){var d=void 0===arguments[1]?{}:arguments[1],e=[],f=!1,g=0,h=void 0,i=void 0,j=void 0,k=void 0,l=void 0,m=void 0,n=void 0,o=void 0,p=void 0,q=void 0,r=void 0;if(isNaN(a))throw new Error("Invalid arguments");return j=d.bits===!0,p=d.unix===!0,i=void 0!==d.base?d.base:2,o=void 0!==d.round?d.round:p?1:2,q=void 0!==d.spacer?d.spacer:p?"":" ",r=void 0!==d.suffixes?d.suffixes:{},n=void 0!==d.output?d.output:"string",h=void 0!==d.exponent?d.exponent:-1,m=Number(a),l=0>m,k=i>2?1e3:1024,l&&(m=-m),0===m?(e[0]=0,e[1]=p?"":"B"):((-1===h||isNaN(h))&&(h=Math.floor(Math.log(m)/Math.log(k))),h>8&&(g=1e3*g*(h-8),h=8),g=2===i?m/Math.pow(2,10*h):m/Math.pow(1e3,h),j&&(g=8*g,g>k&&(g/=k,h++)),e[0]=Number(g.toFixed(h>0?o:0)),e[1]=c[j?"bits":"bytes"][h],!f&&p&&(j&&b.test(e[1])&&(e[1]=e[1].toLowerCase()),e[1]=e[1].charAt(0),"B"===e[1]?(e[0]=Math.floor(e[0]),e[1]=""):j||"k"!==e[1]||(e[1]="K"))),l&&(e[0]=-e[0]),e[1]=r[e[1]]||e[1],"array"===n?e:"exponent"===n?h:"object"===n?{value:e[0],suffix:e[1]}:e.join(q)};"undefined"!=typeof exports?module.exports=d:"function"==typeof define?define(function(){return d}):a.filesize=d}("undefined"!=typeof global?global:window);
|
||||
//# sourceMappingURL=filesize.min.js.map
|
||||
1
frontend/delta/js/Filesize/filesize.min.js.map
Normal file
1
frontend/delta/js/Filesize/filesize.min.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user