1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-29 18:37:35 +01:00

Interim synchronization with internal repository

This is an intermir commit to share what is going on with the development of the new /delta version.
This commit is contained in:
Giulio Cesare Solaroli
2014-07-28 18:07:48 +02:00
parent 6dd16d9359
commit f8da092f3d
111 changed files with 34049 additions and 28666 deletions

View File

@@ -30,6 +30,7 @@ Clipperz.PM.DataModel.Record = function(args) {
Clipperz.PM.DataModel.Record.superclass.constructor.apply(this, arguments);
this._updateDate = (args.updateDate ? Clipperz.PM.Date.parse(args.updateDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
this._accessDate = (args.accessDate ? Clipperz.PM.Date.parse(args.accessDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
this._retrieveIndexDataFunction = args.retrieveIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
this._updateIndexDataFunction = args.updateIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
@@ -40,6 +41,8 @@ Clipperz.PM.DataModel.Record = function(args) {
this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
this._tags = [];
this._directLogins = {};
this._versions = {};
@@ -128,22 +131,109 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return deferredResult;
},
//=========================================================================
//============================================================================
/*
'key': function () {
return this.getIndexDataForKey('key');
},
*/
//=========================================================================
//============================================================================
'fullLabel': function () {
return this.getIndexDataForKey('label');
},
'updateFullLabelWithTags': function (someTags) {
return Clipperz.Async.callbacks("Record.updateFullLabelWithTags", [
MochiKit.Base.method(this, 'label'),
function (aLabel) {
return aLabel + ' ' + MochiKit.Base.map(function (aTag) { return Clipperz.PM.DataModel.Record.tagChar + aTag; }, MochiKit.Base.keys(someTags)).join(' ');
},
MochiKit.Base.method(this, 'setIndexDataForKey', 'label')
], {trace:false});
},
//............................................................................
'tagChar': function () {
return Clipperz.PM.DataModel.Record.tagChar;
},
'tagRegExp': function () {
return new RegExp('\\' + this.tagChar() + '(\\w+)', 'g');
},
'trimSpacesRegExp': function () {
return new RegExp('^\\s+|\\s+$', 'g');
},
//............................................................................
'filterOutTags': function (aValue) {
var value;
value = aValue;
value = value.replace(this.tagRegExp(), '');
value = value.replace(this.trimSpacesRegExp(), '');
return value;
},
'label': function () {
return this.getIndexDataForKey('label');
return Clipperz.Async.callbacks("Record.label", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'filterOutTags')
], {trace:false});
},
'setLabel': function (aValue) {
return this.setIndexDataForKey('label', aValue); // [???]
},
//.........................................................................
'setLabel': function (aValue) {
return this.setIndexDataForKey('label', aValue);
'extractTags': function (aLabel) {
var tagRegEx;
var result;
var match;
result = {};
tagRegEx = this.tagRegExp();
match = tagRegEx.exec(aLabel);
while (match != null) {
result[match[1]] = true;
match = tagRegEx.exec(aLabel);
}
return result;
},
'tags': function () {
return Clipperz.Async.callbacks("Record.label", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTags'),
MochiKit.Base.keys
], {trace:false});
},
'addTag': function (aNewTag) {
//console.log("ADD TAG", aNewTag);
return Clipperz.Async.callbacks("Record.addTag", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTags'),
function (someTags) { someTags[aNewTag] = true; console.log("UPDATED TAGS", someTags); return someTags; },
MochiKit.Base.method(this, 'updateFullLabelWithTags')
], {trace:false});
},
'removeTag': function (aTag) {
//console.log("ADD TAG", aNewTag);
return Clipperz.Async.callbacks("Record.removeTag", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTags'),
function (someTags) { delete someTags[aTag]; return someTags; },
MochiKit.Base.method(this, 'updateFullLabelWithTags')
], {trace:false});
},
//=========================================================================
@@ -183,6 +273,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return MochiKit.Async.succeed(this._updateDate);
},
'accessDate': function () {
return MochiKit.Async.succeed(this._accessDate);
},
//=========================================================================
'favicon': function () {
@@ -208,7 +302,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
deferredResult = new Clipperz.Async.Deferred("Record.searchableContent", {trace:false});
deferredResult.collectResults({
'recordLabel': MochiKit.Base.method(this, 'label'),
'recordLabel': MochiKit.Base.method(this, 'fullLabel'),
'directLoginLabels': [
MochiKit.Base.method(this, 'directLoginReferences'),
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.itemgetter('label'))
@@ -889,3 +983,20 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
});
Clipperz.PM.DataModel.Record.defaultCardInfo = {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
'_searchableContent': MochiKit.Base.methodcaller('searchableContent'),
'_accessDate': MochiKit.Base.methodcaller('accessDate'),
'label': MochiKit.Base.methodcaller('label'),
'favicon': MochiKit.Base.methodcaller('favicon')
};
Clipperz.PM.DataModel.Record.defaultSearchField = '_searchableContent';
Clipperz.PM.DataModel.Record.tagChar = '#';
Clipperz.PM.DataModel.Record.regExpForTag = function (aTag) {
return new RegExp('\\' + Clipperz.PM.DataModel.Record.tagChar + aTag, 'g');
};
Clipperz.PM.DataModel.Record.regExpForSearch = function (aSearch) {
return new RegExp(aSearch.replace(/[^A-Za-z0-9]/g, '\\$&'), 'i');
};

View File

@@ -25,26 +25,26 @@ try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catc
throw "Clipperz.PM.DataModel.User.Subscription depends on Clipperz.PM.DataModel.User!";
}
Clipperz.PM.DataModel.User.Subscription = function(args) {
Clipperz.PM.DataModel.User.AccountInfo = function(args) {
this._attributes = args;
return this;
}
Clipperz.Base.extend(Clipperz.PM.DataModel.User.Subscription, Object, {
Clipperz.Base.extend(Clipperz.PM.DataModel.User.AccountInfo, Object, {
'features': function () {
return this._attributes['features'];
},
'type': function () {
return this._attributes['type'];
'featureSet': function () {
return this._attributes['featureSet'];
},
'validity': function () {
return {
'from': this._attributes['fromDate'],
'to': this._attributes['toDate']
// 'from': this._attributes['fromDate'],
'to': this._attributes['expirationDate']
};
},

View File

@@ -133,6 +133,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Legacy, Clipperz.PM.DataM
'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
'updateDate': someObjectData['recordsStats'][reference]['updateDate'],
'accessDate': someObjectData['recordsStats'][reference]['accessDate'],
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),

View File

@@ -29,7 +29,7 @@ if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.Data
Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
Clipperz.PM.DataModel.User.Header.RecordIndex.superclass.constructor.apply(this, arguments);
//console.log("RECORD INDEX ARGS", args);
this._recordsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
'name': 'recordsData',
'retrieveKeyFunction': args.retrieveKeyFunction,
@@ -37,9 +37,7 @@ Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
'data': args.recordsData['data'],
'version': args.encryptedDataVersion,
'recordsStats': args.recordsStats
}//,
// 'encryptedDataKeypath': 'data',
// 'encryptedVersionKeypath': 'version'
}
});
this._directLoginsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
@@ -48,11 +46,10 @@ Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
'remoteData': {
'data': args.directLoginsData['data'],
'version': args.encryptedDataVersion
}//,
// 'encryptedDataKeypath': 'data',
// 'encryptedVersionKeypath': 'version'
}
});
this._tagsData =
this._lock = new MochiKit.Async.DeferredLock();
this._transientState = null;
@@ -154,9 +151,6 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
},
'setDirectLoginIndexData': function (aDirectLoginReference, aKey, aValue) {
//if (MochiKit.Base.isUndefinedOrNull(this.directLoginsIndex()[aDirectLoginReference])) {
// throw "PIPPO";
//}
return this.directLoginsData().setValue(this.directLoginsIndex()[aDirectLoginReference] + '.' + aKey, aValue);
},
@@ -182,8 +176,6 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
innerDeferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.records <inner deferred>", {trace:false});
innerDeferredResult.collectResults({
'records': [
// MochiKit.Base.method(this.recordsData(), 'getObjectDataStore'),
// MochiKit.Base.methodcaller('values')
MochiKit.Base.method(this.recordsData(), 'values')
],
'recordsStats': [
@@ -191,8 +183,6 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
MochiKit.Base.itemgetter('recordsStats')
],
'directLogins': [
// MochiKit.Base.method(this.directLoginsData(), 'getObjectDataStore'),
// MochiKit.Base.methodcaller('values')
MochiKit.Base.method(this.directLoginsData(), 'values')
]
})
@@ -210,11 +200,13 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
var record;
var reference;
var updateDate;
var accessDate;
reference = recordsInvertedIndex[indexReference];
if (typeof(someData['recordsStats'][reference]) != 'undefined') {
updateDate = someData['recordsStats'][reference]['updateDate'];
accessDate = someData['recordsStats'][reference]['accessDate'];
record = new Clipperz.PM.DataModel.Record({
'reference': reference,
@@ -224,6 +216,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
'updateDate': updateDate,
'accessDate': accessDate,
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
@@ -235,13 +228,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
this._records[reference] = record;
} else {
Clipperz.log("SKIPPING record " + reference + " as there are no stas associated - " + Clipperz.Base.serializeJSON(someData['records'][reference]));
// # skip the record, as it seems it is not present in the DB
// updateDate = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
}
}
for (indexReference in someData['directLogins']) {
// var directLogin;
var reference;
var record;
@@ -249,7 +239,6 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
record = this._records[recordsInvertedIndex[someData['directLogins'][indexReference]['record']]];
if (record != null) {
// directLogin = new Clipperz.PM.DataModel.DirectLogin({
new Clipperz.PM.DataModel.DirectLogin({
'reference': reference,
'record': record
@@ -301,6 +290,7 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
'accessDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
@@ -384,10 +374,6 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
'deleteAllCleanTextData': function () {
return Clipperz.Async.callbacks("User.Header.RecordIndex.deleteAllCleanTextData", [
// MochiKit.Base.method(this, 'records'),
// MochiKit.Base.values,
// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
MochiKit.Base.method(this, 'recordsData'),
MochiKit.Base.methodcaller('deleteAllCleanTextData'),
MochiKit.Base.method(this, 'directLoginsData'),
@@ -410,21 +396,9 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
MochiKit.Base.method(this, 'directLoginsData'),
MochiKit.Base.methodcaller('hasAnyCleanTextData')
],
// 'records': [
// MochiKit.Base.method(this, 'records'),
// MochiKit.Base.values,
// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
// Clipperz.Async.collectAll
// ]
});
// deferredResult.addCallback(MochiKit.Base.values);
// deferredResult.addCallback(MochiKit.Base.flattenArguments);
// deferredResult.addCallback(function(someValues) {
// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
// });
deferredResult.addCallback(Clipperz.Async.or);
deferredResult.callback();
return deferredResult;
@@ -447,11 +421,6 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
]
});
deferredResult.addCallback(Clipperz.Async.or);
// deferredResult.addCallback(MochiKit.Base.values);
// deferredResult.addCallback(MochiKit.Base.flattenArguments);
// deferredResult.addCallback(function(someValues) {
// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
// });
deferredResult.callback();
return deferredResult;
@@ -482,9 +451,6 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
MochiKit.Base.method(this, 'recordsData'),
MochiKit.Base.methodcaller('revertChanges'),
// MochiKit.Base.method(this, 'directLoginsData'),
// MochiKit.Base.methodcaller('revertChanges'),
MochiKit.Base.method(this, 'records'),
MochiKit.Base.bind(function (someRecords) {
var recordReference;
@@ -500,22 +466,10 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stas associated
}
}, this),
// MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.bind(function () {
var directLoginReference;
// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
//
// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
// for (directLoginReference in this.transientState().getValue('deleteDirectLoginReferences')) {
// someDirectLogins[directLoginReference] = this.transientState().getValue('deleteDirectLoginReferences' + '.' + recordReference);
// }
for (directLoginReference in this.transientState().getValue('newDirectLoginReferences')) {
// this.directLoginsData().removeValue(this.directLoginsIndex()[directLoginReference]);
delete this.directLoginsIndex()[directLoginReference];
}
}, this),

View File

@@ -41,7 +41,7 @@ Clipperz.PM.DataModel.User = function (args) {
this._connection = null;
this._connectionVersion = 'current';
this._subscription = null;
this._accountInfo = null;
this._serverData = null;
// this._serverLockValue = null;
this._transientState = null;
@@ -78,12 +78,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
//-------------------------------------------------------------------------
// this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(someServerData['subscription']));
'subscription': function () {
return this._subscription;
'accountInfo': function () {
return this._accountInfo;
},
'setSubscription': function (aValue) {
this._subscription = aValue;
'setAccountInfo': function (aValue) {
this._accountInfo = aValue;
},
//-------------------------------------------------------------------------
@@ -247,19 +247,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false});
// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
deferredResult.addMethod(this, 'getPassphrase');
deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue);
deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [
// MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}),
MochiKit.Base.method(this, 'getCredentials'),
MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'),
MochiKit.Base.method(this.data(), 'setValue', 'passphrase')
], []));
deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase'));
deferredResult.addMethod(this.connection(), 'login', false);
deferredResult.addMethod(this, 'setupConnectionInfo');
// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn');
deferredResult.addMethod(this, 'setupAccountInfo');
deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
deferredResult.callback();
@@ -295,9 +292,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
//-------------------------------------------------------------------------
'setupConnectionInfo': function (aValue) {
'setupAccountInfo': function (aValue) {
//console.log("User.setupAccountInfo", aValue, aValue['accountInfo']);
// this.setLoginInfo(aValue['loginInfo']);
this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(aValue['subscription']));
this.setAccountInfo(new Clipperz.PM.DataModel.User.AccountInfo(aValue['accountInfo']));
},
//-------------------------------------------------------------------------
@@ -511,6 +509,19 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
//=========================================================================
'getTags': function () {
return Clipperz.Async.callbacks("User.getTags", [
MochiKit.Base.method(this, 'getRecords'),
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('tags')),
Clipperz.Async.collectAll,
MochiKit.Base.flattenArray,
Clipperz.Base.arrayWithUniqueValues
], {trace:false});
},
//=========================================================================
'getRecords': function () {
return Clipperz.Async.callbacks("User.getRecords", [
MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
@@ -519,6 +530,36 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
], {trace:false});
},
'getRecordsInfo': function (someInfo, shouldIncludeArchivedCards) {
return Clipperz.Async.callbacks("User.getRecordsInfo", [
MochiKit.Base.method(this, 'getRecords'),
MochiKit.Base.partial(MochiKit.Base.map, Clipperz.Async.collectResults("collectResults", someInfo, {trace:false})),
Clipperz.Async.collectAll
], {trace:false});
},
/*
'filterRecordsInfo': function (someArgs) {
var info = (someArgs.info ? someArgs.info : Clipperz.PM.DataModel.Record.defaultCardInfo);
var searchField = (someArgs.searchField ? someArgs.searchField : Clipperz.PM.DataModel.Record.defaultSearchField);
var includeArchived = (someArgs.includeArchived ? someArgs.includeArchived : false);
var regExp = (someArgs.regExp ? someArgs.regExp : Clipperz.PM.DataModel.Record.regExpForSearch(''));
if (someArgs.regExp) {
regExp = regExp;
} else if (someArgs.search) {
regExp = Clipperz.PM.DataModel.Record.regExpForSearch(someArgs.search);
} else if (someArgs.tag) {
regExp = Clipperz.PM.DataModel.Record.regExpForTag(someArgs.tag);
} else {
regExp = Clipperz.PM.DataModel.Record.regExpForSearch('');
};
return Clipperz.Async.callbacks("User.filterRecordsInfo", [
MochiKit.Base.method(this, 'getRecordsInfo', info, includeArchived),
MochiKit.Base.partial(MochiKit.Base.filter, function (aCardInfo) { regExp.lastIndex = 0; return regExp.test(aCardInfo[searchField]);})
], {trace:false});
},
*/
'recordWithLabel': function (aLabel) {
return Clipperz.Async.callbacks("User.recordWithLabel", [
MochiKit.Base.method(this, 'getRecords'),

View File

@@ -152,7 +152,7 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
'sendMessage': function (aFunctionName, someParameters) {
var deferredResult;
console.log("PROXY.sendMessage", aFunctionName, someParameters);
//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');

View File

@@ -57,7 +57,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
version: aVersion,
parameters: Clipperz.Base.serializeJSON(someParameters)
};
console.log("PROXY.JSON._sendMessage", parameters);
//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(), {

View File

@@ -36,7 +36,7 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) {
this._tolls = {};
this._currentStaticConnection = null;
return this;
}
@@ -47,7 +47,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
'isReadOnly': function () {
return this._isReadOnly;
},
'canRegisterNewUsers': function () {
return false;
},
@@ -296,14 +296,14 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
throw "user already exists";
}
} else {
throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
}
result = {
result: {
'lock': this.data()['users'][someParameters['credentials']['C']]['lock'],
'result': 'done'
},
},
toll: this.getTollForRequestType('CONNECT')
}
@@ -334,7 +334,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
randomBytes = Clipperz.Crypto.Base.generateRandomSeed();
aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16);
v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()));
aConnection['B'] = (Clipperz.Crypto.SRP.k().multiply(v)).add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()));
aConnection['A'] = someParameters.parameters.A;
@@ -343,21 +343,65 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
nextTollRequestType = 'CONNECT';
} else if (someParameters.message == "credentialCheck") {
var v, u, S, A, K, M1;
var v, u, s, S, A, K, M1;
var stringHash = function (aValue) {
return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aValue)).toHexString().substring(2);
};
v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16);
A = new Clipperz.Crypto.BigInt(aConnection['A'], 16);
u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10))).toHexString(), 16);
s = new Clipperz.Crypto.BigInt(aConnection['userData']['s'], 16);
S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n());
K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2);
K = stringHash(S.asString(10));
M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2);
M1 = stringHash(
"597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" +
stringHash(aConnection['C']) +
s.asString(10) +
A.asString(10) +
aConnection['B'].asString(10) +
K
);
if (someParameters.parameters.M1 == M1) {
var M2;
M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2);
M2 = stringHash(
A.asString(10) +
someParameters.parameters.M1 +
K
);
result['M2'] = M2;
result['accountInfo'] = {
'currentSubscriptionType': "FAN",
'expirationDate': "Tue, 21 April 2015 11:59:12 UTC",
'featureSet': "FULL",
'features': [
'UPDATE_CREDENTIALS',
'EDIT_CARD',
'CARD_DETAILS',
'ADD_CARD',
'DELETE_CARD',
'OFFLINE_COPY',
'LIST_CARDS'
],
'isExpired': false,
'isExpiring': false,
'latestActiveLevel': "PAYING",
'latestActiveThreshold': "5.00000000",
'paymentVerificationPending': false,
'payments': [
{
'amount': "0.08500000",
'currency': "BTC",
'date': "Mon, 21 April 2014 12:11:12 UTC",
'reference': "cad577106f8747ae1b0fad3f139f4b4644301a0608dd931f758ad18c1766cabe",
'value': "5.23730000"
}
],
'referenceDate': "Tue, 22 July 2014 15:47:08 UTC"
};
} else {
throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error");
}
@@ -426,6 +470,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
recordsStats = {};
for (recordReference in aConnection['userData']['records']) {
recordsStats[recordReference] = {
'accessDate': aConnection['userData']['records'][recordReference]['accessDate'],
'updateDate': aConnection['userData']['records'][recordReference]['updateDate']
}
}

View File

@@ -0,0 +1,56 @@
/*
Copyright 2008-2013 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.UI.Components');
Clipperz.PM.UI.Components.AccountStatus = React.createClass({
propTypes: {
'currentSubscriptionType': React.PropTypes.oneOf(['EARLY_ADOPTER', 'FRIEND', 'FAN', 'DEVOTEE', 'PATRON', 'TRIAL', 'TRIAL_EXPIRED', 'PAYMENT_FAILED_2', 'EXPIRED', 'PAYMENT_FAILED', 'VERIFYING_PAYMENT', 'VERIFYING_PAYMENT_2']).isRequired,
'expirationDate': React.PropTypes.string.isRequired,
'featureSet': React.PropTypes.oneOf(['TRIAL', 'EXPIRED', 'FULL']).isRequired,
'isExpired': React.PropTypes.bool.isRequired,
'isExpiring': React.PropTypes.bool.isRequired,
'paymentVerificationPending': React.PropTypes.bool.isRequired,
},
//=========================================================================
render: function () {
//console.log("AccountStatus props", this.props);
var classes = {
'accountStatus': true,
'isExpiring': this.props['isExpiring'],
'isExpired': this.props['isExpired'],
};
classes[this.props['featureSet']] = true;
return React.DOM.div({'className':React.addons.classSet(classes)}, [
React.DOM.span({'className': 'level'}, this.props['featureSet']),
React.DOM.span({'className': 'expirationDate'}, this.props['expirationDate'])
]);
}
//=========================================================================
});

View File

@@ -0,0 +1,53 @@
/*
Copyright 2008-2013 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.UI.Components');
Clipperz.PM.UI.Components.Button = React.createClass({
propTypes: {
'eventName': React.PropTypes.string.isRequired,
'label': React.PropTypes.string.isRequired,
'handler': React.PropTypes.func.isRequired,
'className': React.PropTypes.string
},
//=========================================================================
render: function () {
var classes = {
'button': true
};
if (typeof(this.props['className']) != 'undefined') {
classes[this.props['className']] = true;
};
return React.DOM.div({className:React.addons.classSet(classes), onClick:this.props['handler']}, [
React.DOM.div({className:this.props['eventName']}, [
React.DOM.h3({className:'label'}, this.props['label'])
])
]);
}
//=========================================================================
});

View File

@@ -1,195 +0,0 @@
/*
Copyright 2008-2013 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.PM.UI.Components.CardDetail = React.createClass({
getDefaultProps: function () {
return {
// searchDelay: 0.3
}
},
propTypes: {
card: React.PropTypes.object.isRequired
},
getInitialState: function () {
return {
// showSearch: false,
// searchTimer: null,
unmaskedFields: new Clipperz.Set(),
starred: false
};
},
handleDirectLoginClick: function (aDirectLoginReference, anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference});
},
toggleFieldVisibility: function (aField, anEvent) {
var unmaskedFields;
var fieldReference;
unmaskedFields = this.state['unmaskedFields'];
fieldReference = aField['reference']
if (unmaskedFields.contains(fieldReference)) {
unmaskedFields.remove(fieldReference)
} else {
unmaskedFields.add(fieldReference)
}
this.setState({'unmaskedFields': unmaskedFields});
},
handleGoAction: function (aField, anEvent) {
var newWindow;
newWindow = MochiKit.DOM.currentWindow().open(aField['value'], '_blank');
newWindow.focus();
},
handleEmailAction: function (aField, anEvent) {
MochiKit.DOM.currentWindow().location = 'mailto:' + aField['value'];
},
//=========================================================================
normalizeFieldValue: function (aValue) {
var result = [];
var rows = aValue.split('\n');
for (var i = 0; i < rows.length; i++) {
if (i > 0) {
result.push(React.DOM.br());
}
result.push(rows[i].replace(/[\s]/g, '\u00A0'));
}
return result;
},
renderFieldActionButton: function (aField) {
// var actionLabel;
var result;
if (aField['actionType'] == 'URL') {
result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleGoAction', aField)}, [
React.DOM.a({className:aField['actionType']}, "go")
]);
} else if (aField['actionType'] == 'PASSWORD') {
var icon;
if (this.state['unmaskedFields'].contains(aField['reference'])) {
icon = "unlocked";
} else {
icon = "locked";
}
result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'toggleFieldVisibility', aField)}, [
React.DOM.a({className:aField['actionType']}, icon)
]);
} else if (aField['actionType'] == 'EMAIL') {
result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleEmailAction', aField)}, [
React.DOM.a({className:aField['actionType']}, "email")
]);
} else {
result = null;
}
return result;
},
renderField: function (aField) {
//console.log("FIELD", aField);
var fieldExtraClass;
fieldExtraClass = aField['actionType'];
if (this.state['unmaskedFields'].contains(aField['reference'])) {
fieldExtraClass = fieldExtraClass + ' unlocked';
}
return React.DOM.div({className:'listItem ' + fieldExtraClass, key:aField['reference']}, [
React.DOM.div({className:'fieldWrapper'}, [
React.DOM.div({className:'fieldInnerWrapper'}, [
React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aField['label'])),
React.DOM.div({className:'valueWrapper'}, React.DOM.span({className:'value ' + fieldExtraClass}, this.normalizeFieldValue(aField['value'])))
])
]),
this.renderFieldActionButton(aField)
// React.DOM.div({className:'actionWrapper'}, [
// React.DOM.div({className:aField['actionType']}, actionLabel)
// ])
]);
},
renderDirectLogin: function (aDirectLogin) {
//console.log("DIRECT LOGIN", aDirectLogin);
return React.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleDirectLoginClick', aDirectLogin['reference'])}, [
React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aDirectLogin['label'])),
React.DOM.div({className:'faviconWrapper'}, React.DOM.img({className:'favicon', src:aDirectLogin['favicon']})),
React.DOM.div({className:'directLoginLinkWrapper'}, React.DOM.span({className:'directLoginLink'}, "go"))
]);
},
handleBackClick: function (anEvent) {
// window.history.back();
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
},
handleStarClick: function (anEvent) {
this.setState({starred: !this.state['starred']});
},
//=========================================================================
render: function () {
var card = this.props.card;
// var starredStatus = (this.state['starred'] ? "starred" : "unstarred");
if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) {
card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] })
}
return React.DOM.div({className:'cardDetail'}, [
React.DOM.div({className:'header'}, [
React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title)),
React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
// React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus))
]),
React.DOM.div({className:'content'}, [
card.fields ? React.DOM.div({className:'fields'}, MochiKit.Base.map(this.renderField, card.fields)) : null,
card.directLogins ? React.DOM.div({className:'directLogins'}, MochiKit.Base.map(this.renderDirectLogin, card.directLogins)): null
]),
React.DOM.div({className:'footer'}, [
/*
// React.DOM.a({className:'cancel'}, "cancel"),
// React.DOM.a({className:'save'}, "save")
React.DOM.a({className:'cancel button'}, "failed"),
React.DOM.a({className:'save button'}, "done")
*/
])
]);
}
//=========================================================================
});

View File

@@ -1,168 +0,0 @@
/*
Copyright 2008-2013 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.PM.UI.Components.CardList = React.createClass({
getDefaultProps: function () {
return {
selectedCard: null,
searchDelay: 0.3
}
},
propTypes: {
searchDelay: React.PropTypes.number
},
getInitialState: function () {
return {
showSearch: false,
searchTimer: null,
searchText: '',
// passphrase: '',
// pin: ''
};
},
//=========================================================================
toggleSearch: function (anEvent) {
var showSearchBox;
showSearchBox = !this.state.showSearch;
this.setState({showSearch: showSearchBox});
if (showSearchBox) {
MochiKit.Async.callLater(0.1, MochiKit.Base.method(this, 'focusOnSearchField'));
}
},
updateSearchText: function (anEvent) {
var searchText;
searchText = anEvent.target.value;
//console.log(">>> updateSearchText", searchText);
if ((this.state['searchTimer'] != null) && (searchText != this.state['searchText'])) {
this.state['searchTimer'].cancel();
}
if (searchText != this.state['searchText']) {
this.state['searchText'] = searchText;
this.state['searchTimer'] = MochiKit.Async.callLater(this.props['searchDelay'], MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'searchCards', searchText);
}
},
focusOnSearchField: function () {
console.log("focusOnSearchField", this.refs['searchField']);
this.refs['searchField'].getDOMNode.focus();
},
searchBox: function () {
var result;
if (this.state.showSearch) {
result = React.DOM.div({className:'searchBox'}, [
React.DOM.div(null, [
React.DOM.input({type:'search', placeholder:"search", ref:'searchField', onChange:this.updateSearchText})
])
]);
} else {
result = null;
}
return result;
},
//=========================================================================
showPreferences: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showPreferences', anEvent);
},
//=========================================================================
cardItem: function (aRecordReference) {
var reference = aRecordReference['_reference'];
var selectedCard = (reference == this.props.selectedCard);
// TODO: verify if it is possible to put the onClick handler on the container 'div', instead of adding it to each 'div' item.
return React.DOM.div({className:'listItem', key:reference, onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [
React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label)),
// React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label)),
React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')),
React.DOM.div({className:'detailLinkWrapper'}, React.DOM.span({className:'detailLink ' + (selectedCard ? 'icon-spin' : '')}, (selectedCard ? "loading" : "detail")))
]);
},
handleClickOnCardDetail: function (aRecordReference, anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRecord', aRecordReference);
},
cardListItems: function () {
var list;
var result;
list = this.props['cardList'];
if (typeof(list) != 'undefined') {
result = MochiKit.Base.map(MochiKit.Base.method(this, 'cardItem'), list);
} else {
result = null;
}
return result;
},
//=========================================================================
handleChange: function (anEvent) {
// var refs = this.refs;
// var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
// var newState = {};
//
// newState[refName] = event.target.value;
// this.setState(newState);
},
//=========================================================================
render: function() {
return React.DOM.div(null, [
React.DOM.div({className:'header'}, [
React.DOM.a({className:'account'}, 'clipperz'),
React.DOM.div({className:'features'}, [
// React.DOM.a({className:'addCard'}, 'add'),
React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'),
React.DOM.a({className:'settings', onClick:this.showPreferences}, 'settings')
]),
// this.searchBox()
]),
this.searchBox(),
React.DOM.div({className:'content cardList'}, this.cardListItems()),
]);
}
//=========================================================================
});

View File

@@ -0,0 +1,101 @@
/*
Copyright 2008-2013 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.UI.Components');
Clipperz.PM.UI.Components.CardToolbar = React.createClass({
propTypes: {
// 'style': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
'enableSidePanels': React.PropTypes.bool.isRequired,
'accountStatus': React.PropTypes.object.isRequired,
'messageBox': React.PropTypes.object.isRequired,
'filter': React.PropTypes.object.isRequired
},
//----------------------------------------------------------------------------
selectionToggleHandler: function (anEvent) {
//console.log("selectionToggleHandler");
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleSelectionPanel');
},
settingsToggleHandler: function (anEvent) {
//console.log("settingsToggleHandler");
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleSettingsPanel');
},
//============================================================================
renderWithSidePanels: function () {
return [
React.DOM.div({className:'selectionToggle'}, [
Clipperz.PM.UI.Components.Button({eventName:'selectionToggleButton', label:"tags", handler:this.selectionToggleHandler})
]),
this.renderWithoutSidePanels(),
React.DOM.div({className:'settingsToggle'}, [
Clipperz.PM.UI.Components.Button({eventName:'settingsToggleButton', label:"menu", handler:this.settingsToggleHandler})
])
];
},
renderWithoutSidePanels: function () {
var result;
if (this.props['filter']) {
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'] == 'TAG') {
result = [React.DOM.div({className:'clipperz'}, [
React.DOM.span({className:'logo tag'}, "tag"),
React.DOM.span({className:'value'}, this.props['filter']['value'])
])];
} else if (this.props['filter']['type'] == 'SEARCH') {
result = [React.DOM.div({className:'clipperz'}, [
React.DOM.span({className:'logo search'}, "search"),
React.DOM.span({className:'value'}, this.props['filter']['value'])
])];
} else {
result = [React.DOM.div({className:'clipperz'}, [React.DOM.span({className:'logo clipperz'}, "clipperz")])];
}
} else {
result = [React.DOM.div({className:'clipperz'}, [React.DOM.span({className:'logo clipperz'}, "clipperz")])];
}
return result;
},
render: function () {
//console.log("CardToolbar props", this.props);
return React.DOM.div({className:'cardToolbar ' + this.props['style']}, [
// React.DOM.div({className:'header'}, this.props['enableSidePanels'] ? this.renderWithSidePanels() : this.renderWithoutSidePanels()),
React.DOM.header({}, this.props['enableSidePanels'] ? this.renderWithSidePanels() : this.renderWithoutSidePanels()),
Clipperz.PM.UI.Components.AccountStatus(this.props['accountStatus']),
Clipperz.PM.UI.Components.MessageBox(this.props['messageBox']),
]);
}
//============================================================================
});

View File

@@ -0,0 +1,66 @@
/*
Copyright 2008-2013 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.Cards');
Clipperz.PM.UI.Components.Cards.List = React.createClass({
//=========================================================================
propTypes: {
'cards': React.PropTypes.array,
'selectedCard': React.PropTypes.string
},
handleClick: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cardSelected', {'reference':anEvent.currentTarget.dataset.reference, 'label':anEvent.currentTarget.dataset.label});
},
renderItem: function (anItem) {
var classes = {
'selected': this.props['selectedCard'] ? this.props['selectedCard']['_reference'] == anItem['_reference'] : false
};
return React.DOM.li({'className':React.addons.classSet(classes), 'onClick': this.handleClick, 'key':anItem['_reference'], 'data-reference':anItem['_reference'], 'data-label':anItem['label']}, [
React.DOM.span({'className':'favicon'}, [ React.DOM.img({src:anItem['favicon']})]),
React.DOM.span({'className':'label'}, anItem['label']),
// React.DOM.span({'className':'action'}, 'show detail')
]);
},
render: function () {
var cards = this.props['cards'] ? this.props['cards'] : [];
var classes = {
'cardList': true,
'loadingCard': this.props['selectedCard'] && this.props['selectedCard']['_reference'] && this.props['selectedCard']['loading']
};
classes[this.props['style']] = true;
return React.DOM.div({'key':'cardList', 'className':React.addons.classSet(classes)}, [
React.DOM.ul({}, MochiKit.Base.map(this.renderItem, cards))
]);
},
//=========================================================================
});

View File

@@ -0,0 +1,133 @@
/*
Copyright 2008-2013 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.Cards');
Clipperz.PM.UI.Components.Cards.Toolbar = React.createClass({
//============================================================================
propTypes: {
// 'label': React.PropTypes.string.isRequired,
// 'loading': React.PropTypes.bool,
},
//----------------------------------------------------------------------------
getInitialState: function() {
return {'showCommandMenu': false };
},
//----------------------------------------------------------------------------
commands: function () {
return {
'delete': {
'label': "delete",
'broadcastEvent': 'deleteCard'
},
'archive': {
'label': "archive",
'broadcastEvent': 'archiveCard'
},
// 'share': {
// 'label': "share",
// 'broadcastEvent': 'shareCard'
// },
'edit': {
'label': "edit",
'broadcastEvent': 'editCard'
}
};
},
//----------------------------------------------------------------------------
exit: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBackToMainPage', {'reference':this.props['_reference']});
},
toggleMenu: function (anEvent) {
this.setState({'showCommandMenu': !this.state['showCommandMenu'] });
},
selectCommandItem: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, anEvent.target.dataset['broadcastEvent'], {'reference':this.props['_reference']});
},
//----------------------------------------------------------------------------
renderCommands: function () {
var commandHandler = this.selectCommandItem;
return React.DOM.ul({}, MochiKit.Base.map(function (aCommand) {
return React.DOM.li({}, [React.DOM.span({'onClick':commandHandler, 'data-broadcast-event':aCommand['broadcastEvent']}, aCommand['label'])]);
}, MochiKit.Base.values(this.commands())));
},
//----------------------------------------------------------------------------
renderNarrow: function () {
return [
React.DOM.div({}, [
React.DOM.div({'className':'back', 'onClick': this.exit}, 'back'),
React.DOM.div({'className':'cardMenuOptions', 'onClick':this.toggleMenu}, 'commands'),
React.DOM.div({'className':React.addons.classSet({'commandMenu':true, 'show':this.state['showCommandMenu']})}, [
React.DOM.div({'className':'commandMenuMask', 'onClick':this.toggleMenu}),
React.DOM.div({'className':'commandMenu'}, this.renderCommands())
])
])
]
},
renderOther: function () {
return [this.renderCommands()];
},
//----------------------------------------------------------------------------
renderLayout: function (aLayout) {
var result;
if (aLayout == 'narrow') {
result = this.renderNarrow();
} else {
result = this.renderOther();
}
return result;
},
render: function () {
var style = this.props['style'];
var classes = {
'cardDetailToolbar': true,
};
classes[style] = true;
return React.DOM.div({'className':React.addons.classSet(classes)}, this.renderLayout(style));
},
//=========================================================================
});

View File

@@ -0,0 +1,161 @@
/*
Copyright 2008-2013 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.Cards');
Clipperz.PM.UI.Components.Cards.View = React.createClass({
//============================================================================
propTypes: {
'label': React.PropTypes.string.isRequired,
'loading': React.PropTypes.bool,
},
//----------------------------------------------------------------------------
renderEmpty: function () {
return React.DOM.h4({}, "EMPTY");
},
//----------------------------------------------------------------------------
renderLoading: function () {
return React.DOM.div({className:'loading'},[
this.renderLabel(),
React.DOM.h5({className:'message'}, "loading")
/*
React.DOM.div({className:'overlay'}, [
React.DOM.div({className:'spinner'}, [
React.DOM.div({className:'bar01'}),
React.DOM.div({className:'bar02'}),
React.DOM.div({className:'bar03'}),
React.DOM.div({className:'bar04'}),
React.DOM.div({className:'bar05'}),
React.DOM.div({className:'bar06'}),
React.DOM.div({className:'bar07'}),
React.DOM.div({className:'bar08'}),
React.DOM.div({className:'bar09'}),
React.DOM.div({className:'bar10'}),
React.DOM.div({className:'bar11'}),
React.DOM.div({className:'bar12'})
])
])
*/
]);
},
//----------------------------------------------------------------------------
renderLabel: function (aLabel) {
return React.DOM.h3({'className':'cardLabel'}, aLabel);
},
renderNotes: function (someNotes) {
return React.DOM.div({'className':'cardNotes'}, someNotes);
},
//............................................................................
renderTag: function (aTag) {
return React.DOM.div({'className':'cardTag'}, aTag);
},
renderTags: function (someTags) {
return React.DOM.div({'className':'cardTags'}, MochiKit.Base.map(this.renderTag, someTags));
},
//............................................................................
renderField: function (aField) {
var cardFieldClasses = {};
var cardFieldValueClasses = {};
cardFieldClasses['cardField'] = true;
cardFieldClasses[aField['actionType']] = true;
cardFieldClasses['hidden'] = aField['isHidden'];
cardFieldValueClasses['fieldValue'] = true;
cardFieldValueClasses[aField['actionType']] = true;
cardFieldValueClasses['hidden'] = aField['isHidden'];
return React.DOM.div({'className':React.addons.classSet(cardFieldClasses)}, [
React.DOM.div({'className':'fieldValues'}, [
React.DOM.div({'className':'fieldLabel'}, aField['label']),
React.DOM.div({'className':React.addons.classSet(cardFieldValueClasses)}, aField['value']),
]),
React.DOM.div({'className':'fieldAction action'}, aField['actionType'].toLowerCase())
]);
},
renderFields: function (someFields) {
return React.DOM.div({'className':'cardFields'}, MochiKit.Base.map(this.renderField, someFields));
},
//............................................................................
renderDirectLogin: function (aDirectLogin) {
return React.DOM.div({'className':'cardDirectLogin'}, [
React.DOM.span({'className':'directLoginLabel'}, aDirectLogin['label']),
React.DOM.div({'className':'directLoginAction action'}, 'DIRECT LOGIN')
]);
},
renderDirectLogins: function (someDirectLogins) {
return React.DOM.div({'className':'cardDirectLogins'}, MochiKit.Base.map(this.renderDirectLogin, someDirectLogins));
},
//............................................................................
renderCard: function () {
return React.DOM.div({'className':'view'},[
Clipperz.PM.UI.Components.Cards.Toolbar(this.props),
React.DOM.div({'className':'content'}, [
this.renderLabel(this.props['label']),
this.renderTags(this.props['tags']),
this.renderNotes(this.props['notes']),
this.renderFields(this.props['fields']),
this.renderDirectLogins(this.props['directLogins'])
])
]);
},
//----------------------------------------------------------------------------
render: function () {
var result;
if (this.props['loading'] == true) {
result = this.renderLoading();
} else if (this.props['label']) {
result = this.renderCard();
} else {
result = this.renderEmpty();
}
return result;
},
//=========================================================================
});

View File

@@ -21,6 +21,8 @@ refer to http://www.clipperz.com.
*/
Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.Checkbox = React.createClass({
// http://development.tobypitman.com/iphoneCheckboxes/iphoneCheckboxes2.html

View File

@@ -0,0 +1,40 @@
/*
Copyright 2008-2013 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.UI.Components');
Clipperz.PM.UI.Components.ExpiredPanel = React.createClass({
propTypes: {
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired
},
//=========================================================================
render: function () {
return React.DOM.div({className:'expiredPanel'}, "EXPIRED PANEL");
},
//=========================================================================
});

View File

@@ -0,0 +1,47 @@
/*
Copyright 2008-2013 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.UI.Components');
Clipperz.PM.UI.Components.MessageBox = React.createClass({
propTypes: {
'level': React.PropTypes.oneOf(['HIDE', 'INFO', 'WARNING', 'ERROR']).isRequired,
'message': React.PropTypes.string.isRequired
},
getDefaultProps: function () {
return {
level: 'HIDE',
message: ''
};
},
//=========================================================================
render: function () {
return React.DOM.div({className:'messageBox ' + this.props['level']}, this.props['message']);
}
//=========================================================================
});

View File

@@ -21,13 +21,13 @@ refer to http://www.clipperz.com.
*/
Clipperz.PM.UI.Components.PageTemplate = React.createClass({
render: function() {
return React.DOM.div(null, [
React.DOM.div({'className': 'header'}, [
React.DOM.h1(null, "clipperz")
]),
React.DOM.div({'className': 'content'}, this.props.innerComponent)
])
}
Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.CardDetailPage = React.createClass({
render: function () {
// return React.DOM.header({'className':''})
return Clipperz.PM.UI.Components.Cards.View(this.props['selectedCard']);
},
});

View File

@@ -21,11 +21,13 @@ refer to http://www.clipperz.com.
*/
Clipperz.PM.UI.Components.ErrorPage = React.createClass({
Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.ErrorPage = React.createClass({
getDefaultProps: function () {
return {
template: Clipperz.PM.UI.Components.PageTemplate
// template: Clipperz.PM.UI.Components.PageTemplate
}
},
@@ -36,11 +38,11 @@ Clipperz.PM.UI.Components.ErrorPage = React.createClass({
},
_render: function () {
render: function () {
return React.DOM.div({className:'error-message'}, this.props.message);
},
render: function () {
return new this.props.template({'innerComponent': this._render()});
}
// render: function () {
// return new this.props.template({'innerComponent': this._render()});
// }
});

View File

@@ -21,24 +21,26 @@ refer to http://www.clipperz.com.
*/
Clipperz.PM.UI.Components.LoginForm = React.createClass({
"use strict";
Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.LoginPage = React.createClass({
propTypes: {
mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']).isRequired,
isNewUserRegistrationAvailable: React.PropTypes.bool.isRequired,
disabled: React.PropTypes.bool.isRequired
},
/*
getDefaultProps: function () {
return {
mode: 'CREDENTIALS',
isNewUserRegistrationAvailable: false,
disabled: false,
template: Clipperz.PM.UI.Components.PageTemplate
// template: Clipperz.PM.UI.Components.PageTemplate
}
},
propTypes: {
mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']),
isNewUserRegistrationAvailable: React.PropTypes.bool,
disabled: React.PropTypes.bool,
template: React.PropTypes.func
},
*/
getInitialState: function () {
return {
username: '',
@@ -54,7 +56,7 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({
var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
var newState = {};
newState[refName] = event.target.value;
newState[refName] = anEvent.target.value;
this.setState(newState);
},
@@ -86,25 +88,21 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({
((this.state['username'] != '') && (this.state['passphrase'] != ''))
||
(this.state['pin'] != '')
) && !this.props['disabled'];
)
&&
!this.props['disabled'];
},
loginForm: function () {
registrationLink = React.DOM.div({'className':'registrationLink'}, [
React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Sign up")
]);
return React.DOM.div({'className':'loginForm credentials'},[
React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [
React.DOM.div(null,[
React.DOM.label({'for' :'name'}, "username"),
React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}),
React.DOM.label({'for' :'passphrase'}, "passphrase"),
React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'})
]),
React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login")
return React.DOM.form({'className':'loginForm credentials', 'onChange':this.handleChange, 'onSubmit':this.handleCredentialSubmit}, [
React.DOM.div(null,[
React.DOM.label({'htmlFor' :'name'}, "username"),
React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}),
React.DOM.label({'htmlFor' :'passphrase'}, "passphrase"),
React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'})
]),
this.props.isNewUserRegistrationAvailable ? registrationLink : null
React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login")
]);
},
@@ -121,14 +119,12 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({
},
pinForm: function () {
return React.DOM.div({'className':'loginForm pin'},[
React.DOM.form({onChange: this.handleChange, onSubmit:this.handlePINSubmit}, [
React.DOM.div(null,[
React.DOM.label({'for':'pin'}, "pin"),
React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'})
]),
React.DOM.button({'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login")
])
return React.DOM.form({'className':'loginForm pin', 'onChange':this.handleChange, 'onSubmit':this.handlePINSubmit}, [
React.DOM.div(null,[
React.DOM.label({'for':'pin'}, "pin"),
React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'})
]),
React.DOM.button({'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login")
]);
},
@@ -145,6 +141,16 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({
},
render: function() {
return new this.props.template({'innerComponent': this.props.mode == 'PIN' ? this.pinForm() : this.loginForm()});
var registrationLink = React.DOM.div({'className':'registrationLink'}, [
React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Sign up")
]);
return React.DOM.div({'className':'loginForm ' + this.props['style']}, [
React.DOM.header({}, 'clipperz'),
React.DOM.div({'className':'form'}, [
this.props.mode == 'PIN' ? this.pinForm() : this.loginForm(),
]),
this.props.isNewUserRegistrationAvailable ? registrationLink : null
]);
}
});

View File

@@ -0,0 +1,66 @@
/*
Copyright 2008-2013 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.Pages');
Clipperz.PM.UI.Components.Pages.MainPage = React.createClass({
getDefaultProps: function () {
return {
}
},
propTypes: {
'messageBox': React.PropTypes.object.isRequired,
// 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']),
'accountStatus': React.PropTypes.object.isRequired,
// 'mediaQueryStyle': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
// 'cards': React.PropTypes.deferred.isRequired
},
getInitialState: function () {
return {
// shouldStoreDataLocally: false
};
},
//=========================================================================
render: function () {
var classes = {
'mainPage': true
};
classes[this.props['style']] = true;
//console.log("MainPage.cards", this.props['cards'], this.props['cards'].state());
return React.DOM.div({className:React.addons.classSet(classes)}, [
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)
]);
}
//=========================================================================
});

View File

@@ -21,7 +21,9 @@ refer to http://www.clipperz.com.
*/
Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.RegistrationPage = React.createClass({
getDefaultProps: function () {
return {
@@ -31,7 +33,7 @@ Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
{name:'TERMS_OF_SERVICE', label:'registration', _label:'terms', description:"Check our terms of service"}
],
disabled: false,
template: Clipperz.PM.UI.Components.PageTemplate
// template: Clipperz.PM.UI.Components.PageTemplate
}
},
@@ -166,16 +168,16 @@ Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
render_CREDENTIALS: function () {
return React.DOM.div(null,[
React.DOM.label({'for':'name'}, "username"),
React.DOM.label({'htmlFor':'name'}, "username"),
React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'/*, value:this.state.username*/}),
React.DOM.label({'for':'passphrase'}, "passphrase"),
React.DOM.label({'htmlFor':'passphrase'}, "passphrase"),
React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'/*, value:this.state.passphrase*/})
]);
},
render_PASSWORD_VERIFICATION: function () {
return React.DOM.div(null,[
React.DOM.label({'for':'verify_passphrase'}, "passphrase"),
React.DOM.label({'htmlFor':'verify_passphrase'}, "passphrase"),
React.DOM.input({'type':'password', 'name':'verify_passphrase', 'ref':'verify_passphrase', 'placeholder':"verify passphrase", 'key':'verify_passphrase'})
]);
},
@@ -183,12 +185,12 @@ Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
render_TERMS_OF_SERVICE: function () {
return React.DOM.div(null, [
React.DOM.div({className:'checkboxBlock'}, [
React.DOM.label({'for':'no_password_recovery'}, "I understand that Clipperz will not be able to recover a lost passphrase."),
React.DOM.label({'htmlFor':'no_password_recovery'}, "I understand that Clipperz will not be able to recover a lost passphrase."),
React.DOM.input({'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'key':'no_password_recovery'}),
React.DOM.p(null, "I understand that Clipperz will not be able to recover a lost passphrase.")
]),
React.DOM.div({className:'checkboxBlock'}, [
React.DOM.label({'for':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."),
React.DOM.label({'htmlFor':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."),
React.DOM.input({'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'key':'agree_terms_of_service'}),
React.DOM.p(null, [
"I have read and agreed to the ",
@@ -208,7 +210,7 @@ Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
]);
},
_render: function () {
render: function () {
return React.DOM.div({'className':'registrationForm'},[
React.DOM.form({onChange: this.handleChange}, [
React.DOM.div({'className':'steps'}, MochiKit.Base.map(this.renderStep, this.props['steps']))
@@ -216,9 +218,9 @@ Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
]);
},
render: function () {
return new this.props.template({'innerComponent': this._render()});
},
// render: function () {
// return new this.props.template({'innerComponent': this._render()});
// },
//=========================================================================

View File

@@ -0,0 +1,78 @@
/*
Copyright 2008-2013 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.UI.Components.Panels');
Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel = React.createClass({
settingsToggleHandler: function (anEvent) {
//console.log("settingsToggleHandler");
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleSettingsPanel');
},
//=========================================================================
render: function () {
var classes = {
'panel': true,
'right': true,
'open': this.props['settingsPanelStatus'] == 'OPEN'
}
return React.DOM.div({key:'extraFeaturesPanel', id:'extraFeaturesPanel', className:React.addons.classSet(classes)}, [
React.DOM.header({}, [
React.DOM.div({className:'settingsToggle'}, [
Clipperz.PM.UI.Components.Button({eventName:'settingsToggleButton', label:"menu", handler:this.settingsToggleHandler})
])
]),
React.DOM.h2({}, "Extra features")
]);
/*
<div id="extraFeaturesPanel" class="panel extraFeatures">
<div class="warnings">
<ul>
<li>Synchronize local data</li>
</ul>
</div>
<ul>
<li>Account</li>
<li>Subscription</li>
</ul>
<ul>
<li>Local Data</li>
<li>OTP</li>
</ul>
<div class="donation">
<a>Make a donation</a>
</div>
</div>
*/
}
//=========================================================================
});

View File

@@ -0,0 +1,193 @@
/*
Copyright 2008-2013 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.Panels');
Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({
//=========================================================================
propTypes: {
'messageBox': React.PropTypes.object.isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
},
getDefaultProps: function () {
return {
featureSet: 'FULL'
};
},
style: function () {
return this.props['style'];
},
featureSet: function () {
return this.props['featureSet'];
},
handleMaskClick: function () {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'maskClick');
},
handleAddCardClick: function () {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'addCardClick');
},
//----------------------------------------------------------------------------
renderToolbarFrame: function (anInnerComponent) {
return React.DOM.div({'className':'cardToolbarFrame'}, [
this.renderToolbar(),
anInnerComponent
]);
},
renderCardFrame: function (firstColumnComponents, secondColumnComponents) {
var addCardButton = React.DOM.div({'className': 'addCardButton', 'onClick':this.handleAddCardClick}, 'add card');
return React.DOM.div({'key':'cardContent', 'className':'cardContent'}, [
React.DOM.div({'className':'cardListColumn column'}, [addCardButton, firstColumnComponents]),
React.DOM.div({'className':'cardDetail column right'}, secondColumnComponents)
])
},
//----------------------------------------------------------------------------
renderToolbar: function () {
var cardToolbarProps;
cardToolbarProps = MochiKit.Base.merge(this.props, {
'key': 'toolbar',
'style': this.style(),
'enableSidePanels': (this.props['featureSet'] != 'EXPIRED')
});
return Clipperz.PM.UI.Components.CardToolbar(cardToolbarProps);
},
renderExpiredPanel: function () {
return this.featureSet() == 'EXPIRED' ? Clipperz.PM.UI.Components.ExpiredPanel(this.props) : null;
},
//----------------------------------------------------------------------------
viewComponentProps: function () {
var result;
result = this.props['selectedCard'];
if (result) {
result['style'] = this.props['style'];
}
return result;
},
renderExtraWide: function () {
return [
React.DOM.div({'className':'selection subpanel'}, [Clipperz.PM.UI.Components.Selections(this.props)]),
React.DOM.div({'className':'cardContent subpanel'}, [
this.renderToolbarFrame(
this.renderCardFrame(
[Clipperz.PM.UI.Components.Cards.List(this.props)],
[
this.renderExpiredPanel(),
Clipperz.PM.UI.Components.Cards.View(this.viewComponentProps())
]
)
)
])
]
},
//----------------------------------------------------------------------------
renderWide: function () {
return [
this.renderToolbarFrame(
this.renderCardFrame(
[Clipperz.PM.UI.Components.Cards.List(this.props)],
[
this.renderExpiredPanel(),
Clipperz.PM.UI.Components.Cards.View(this.viewComponentProps())
]
)
)
];
},
//----------------------------------------------------------------------------
renderNarrow: function () {
return this.renderCardFrame(
this.renderToolbarFrame([
this.renderExpiredPanel(),
Clipperz.PM.UI.Components.Cards.List(this.props),
]),
[Clipperz.PM.UI.Components.Cards.View(this.viewComponentProps())]
);
},
//----------------------------------------------------------------------------
renderLayout: function (aLayout) {
var result;
if (aLayout == 'extra-wide') {
result = this.renderExtraWide();
} else if (aLayout == 'wide') {
result = this.renderWide();
} else if (aLayout == 'narrow') {
result = this.renderNarrow();
} else if (aLayout == 'extra-short') {
result = this.renderNarrow();
} else {
Clipperz.Base.exception.raise('UnknownType');
}
return result;
},
render: function () {
//console.log("MainPanel.cards", this.props['cards']);
var classes = {
'panel': true,
'left': this.props['selectionPanelStatus'] == 'OPEN',
'right': this.props['settingsPanelStatus'] == 'OPEN',
'open': this.props['selectionPanelStatus'] == 'OPEN' || this.props['settingsPanelStatus'] == 'OPEN'
};
classes[this.style()] = true;
return React.DOM.div({'key':'mainPanel', 'id':'mainPanel', 'className':React.addons.classSet(classes)}, [
React.DOM.div({'className':'mask', 'onClick': this.handleMaskClick}),
React.DOM.div({'className':'container'},
// this.style() == 'extra-wide' ? this.renderExtraWide() : this.renderOther()
this.renderLayout(this.style())
)
]);
}
//=========================================================================
});

View File

@@ -0,0 +1,49 @@
/*
Copyright 2008-2013 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.Panels');
Clipperz.PM.UI.Components.Panels.SelectionPanel = React.createClass({
propTypes: {
selectionPanelStatus: React.PropTypes.oneOf(['OPEN', 'CLOSED']).isRequired
},
//=========================================================================
render: function () {
//console.log("SelectionPanel", this.props);
var classes = React.addons.classSet({
'panel': true,
'left': true,
'open': this.props['selectionPanelStatus'] == 'OPEN'
});
return React.DOM.div({'key':'selectionPanel', 'id':'selectionPanel', 'className':classes}, [
Clipperz.PM.UI.Components.Selections(this.props),
]);
}
//=========================================================================
});

View File

@@ -1,88 +0,0 @@
/*
Copyright 2008-2013 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.PM.UI.Components.PreferencePage = React.createClass({
getDefaultProps: function () {
return {
}
},
propTypes: {
// card: React.PropTypes.object.isRequired
// checked: React.PropTypes.boolean.isRequired
},
getInitialState: function () {
// return {
// shouldStoreDataLocally: false
// };
},
handleBackClick: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
},
toggleShouldStoreDataLocally: function (anEvent) {
// this.setState({shouldStoreDataLocally: !this.state['shouldStoreDataLocally']});
Clipperz.PM.DataModel.devicePreferences.setShouldStoreDataLocally(!Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally());
this.setState({});
},
shouldStoreDataLocally: function () {
return Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally();
},
syncNow: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'synchronizeLocalData');
},
//=========================================================================
render: function () {
return React.DOM.div({className:'preferences'}, [
React.DOM.div({className:'header'}, [
React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, "Preferences")),
React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
]),
React.DOM.div({className:'content'}, [
React.DOM.form(null, [
React.DOM.div({className:'section'}, [
React.DOM.h4(null, "Local storage"),
React.DOM.p(null, "Store you account data locally for offline viewing"),
new Clipperz.PM.UI.Components.Checkbox({'id':'shouldStoreLocally_checkbox', 'checked':this.shouldStoreDataLocally(), 'eventHandler':this.toggleShouldStoreDataLocally}),
this.shouldStoreDataLocally() ? React.DOM.div({className:'syncInfo'}, [
// React.DOM.h5(null, "data were never synchronized before"),
React.DOM.a({className:'button', onClick:this.syncNow}, "Sync now")
]) : null
])
])
]),
React.DOM.div({className:'footer'}, [
])
]);
}
//=========================================================================
});

View File

@@ -0,0 +1,57 @@
/*
Copyright 2008-2013 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.Selections = React.createClass({
//=========================================================================
selectAll: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectAllCards');
},
selectRecent: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectRecentCards');
},
render: function () {
//console.log("Selections", this.props);
return React.DOM.div({'key':'selections', 'id':'selections'}, [
React.DOM.ul({'className':'defaultSet'}, [
React.DOM.li({'className':'allCards', onClick: this.selectAll}, "All"),
React.DOM.li({'className':'recentCards', onClick: this.selectRecent}, "Recent")
]),
React.DOM.div({'className':'search'}, [
React.DOM.form({'className':'searchForm'}, [
React.DOM.label({'htmlFor':'searchValue'}, 'search'),
React.DOM.input({'type':'text', 'id':'searchValue', 'name':'search'})
])
]),
React.DOM.ul({'className':'tagList'}, MochiKit.Base.map(function (aTag) { return Clipperz.PM.UI.Components.TagIndexItem({'label':aTag}); }, this.props['tags'] ? this.props['tags'] : []))
]);
}
//=========================================================================
});

View File

@@ -0,0 +1,45 @@
/*
Copyright 2008-2013 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.TagIndexItem = React.createClass({
//=========================================================================
propTypes: {
'label': React.PropTypes.string.isRequired,
},
handleClick: function (anEvent) {
//console.log("TAG INDEX ITEM - handle click TAG", anEvent.target.dataset.tag);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'tagSelected', anEvent.target.dataset.tag);
},
render: function () {
return React.DOM.li({onClick: this.handleClick, 'data-tag':this.props['label']}, this.props['label']);
},
//=========================================================================
});

View File

@@ -23,17 +23,17 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI');
Clipperz.PM.UI.DirectLoginRunner = function(args) {
Clipperz.PM.UI.DirectLoginController = function(args) {
this._directLogin = args['directLogin'] || Clipperz.Base.exception.raise('MandatoryParameter');
this._target = Clipperz.PM.Crypto.randomKey();
return this;
}
MochiKit.Base.update(Clipperz.PM.UI.DirectLoginRunner.prototype, {
MochiKit.Base.update(Clipperz.PM.UI.DirectLoginController.prototype, {
'toString': function() {
return "Clipperz.PM.UI.DirectLoginRunner";
return "Clipperz.PM.UI.DirectLoginController";
},
//-----------------------------------------------------------------------------
@@ -237,19 +237,19 @@ MochiKit.Base.update(Clipperz.PM.UI.DirectLoginRunner.prototype, {
//-----------------------------------------------------------------------------
Clipperz.PM.UI.DirectLoginRunner.openDirectLogin = function (aDirectLogin) {
Clipperz.PM.UI.DirectLoginController.openDirectLogin = function (aDirectLogin) {
var runner;
runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
runner = new Clipperz.PM.UI.DirectLoginController({directLogin:aDirectLogin});
return runner.run();
};
//-----------------------------------------------------------------------------
Clipperz.PM.UI.DirectLoginRunner.testDirectLogin = function (aDirectLogin) {
Clipperz.PM.UI.DirectLoginController.testDirectLogin = function (aDirectLogin) {
var runner;
runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
runner = new Clipperz.PM.UI.DirectLoginController({directLogin:aDirectLogin});
return runner.test();
};

View File

@@ -21,31 +21,65 @@ refer to http://www.clipperz.com.
*/
"use strict";
Clipperz.Base.module('Clipperz.PM.UI');
Clipperz.PM.UI.MainController = function() {
var pages;
var genericPageProperties;
// this._proxy = null;
this._mediaQueryStyle = "narrow";
this._user = null;
this._filter = '';
// this._currentPage = 'loadingPage';
this._filter = {'type':'ALL'};
this._isSelectionPanelOpen = false;
this._isSettingsPanelOpen = false;
this._pageStack = ['loadingPage'];
this._overlay = new Clipperz.PM.UI.Components.Overlay();
pages = {
'loginPage': new Clipperz.PM.UI.Components.LoginForm(),
'registrationPage': new Clipperz.PM.UI.Components.RegistrationWizard(),
'cardListPage': new Clipperz.PM.UI.Components.CardList(),
'cardDetailPage': new Clipperz.PM.UI.Components.CardDetail({card: {}}),
'preferencePage': new Clipperz.PM.UI.Components.PreferencePage(),
'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''})
};
MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages));
this._pages = pages;
this.registerForNotificationCenterEvents();
this._isTouchDevice = ('ontouchstart' in window || 'onmsgesturechange' in window);
this._isDesktop = window.screenX != 0 && !this._isTouchDevice;
this._hasKeyboard = this._isDesktop;
this._closeMaskAction = null;
this._pages = {};
this.renderPages([
'loginPage',
'registrationPage',
'mainPage',
'cardDetailPage',
'errorPage',
]);
this.registerForNotificationCenterEvents([
'doLogin',
'registerNewUser',
'showRegistrationForm',
'goBack',
'toggleSelectionPanel',
'toggleSettingsPanel',
'matchMediaQuery',
'unmatchMediaQuery',
'selectAllCards',
'selectRecentCards',
'tagSelected',
'cardSelected',
'addCardClick',
'deleteCard',
'archiveCard',
'editCard',
'goBackToMainPage',
'maskClick',
]);
MochiKit.Signal.connect(MochiKit.DOM.currentDocument(), 'onselectionchange', this, 'selectionChangeHandler');
return this;
@@ -125,26 +159,30 @@ console.log("THE BROWSER IS OFFLINE");
//=========================================================================
registerForNotificationCenterEvents: function () {
var events = [
'doLogin',
'registerNewUser',
'showRegistrationForm',
'goBack',
'showRecord',
'searchCards',
'showPreferences',
'runDirectLogin',
'synchronizeLocalData'
];
var self = this;
capitaliseFirstLetter: function (aValue) {
return aValue.charAt(0).toUpperCase() + aValue.slice(1);
},
renderPages: function (pages) {
var self = this;
MochiKit.Iter.forEach(pages, function (aPageName) {
//console.log("RENDERING", aPageName);
self._pages[aPageName] = React.renderComponent(
Clipperz.PM.UI.Components.Pages[self.capitaliseFirstLetter(aPageName)](self.pageProperties(aPageName)),
MochiKit.DOM.getElement(aPageName)
);
});
},
MochiKit.Base.map(function (anEvent) {
MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent));
}, events);
registerForNotificationCenterEvents: function (events) {
var self = this;
MochiKit.Iter.forEach(events, function (anEvent) {
MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent + '_handler'));
});
// MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack'));
MochiKit.Signal.connect(window, 'onbeforeunload', MochiKit.Base.method(this, 'shouldExitApp'));
// MochiKit.Signal.connect(window, 'onbeforeunload', MochiKit.Base.method(this, 'shouldExitApp'));
},
//-------------------------------------------------------------------------
@@ -198,11 +236,13 @@ console.log("THE BROWSER IS OFFLINE");
this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable':canRegisterNewUsers});
if (shouldShowRegistrationForm) {
this.showRegistrationForm();
this.showRegistrationForm_handler();
} else {
this.showLoginForm();
}
this.overlay().done("", 0.5);
// this.overlay().done("", 0.5);
this.overlay().hide();
},
//-------------------------------------------------------------------------
@@ -216,7 +256,7 @@ console.log("THE BROWSER IS OFFLINE");
MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus'));
},
showRegistrationForm: function () {
showRegistrationForm_handler: function () {
var currentPage;
var registrationPage;
@@ -230,7 +270,12 @@ console.log("THE BROWSER IS OFFLINE");
//=========================================================================
doLogin: function (event) {
doLogin_handler: function (event) {
return this.doLogin(event);
},
doLogin: function (someCredentials) {
var deferredResult;
var credentials;
var getPassphraseDelegate;
var user;
@@ -240,10 +285,10 @@ console.log("THE BROWSER IS OFFLINE");
this.overlay().show("logging in");
this.pages()['loginPage'].setProps({disabled:true});
if ('pin' in event) {
credentials = Clipperz.PM.PIN.credentialsWithPIN(event['pin']);
if ('pin' in someCredentials) {
credentials = Clipperz.PM.PIN.credentialsWithPIN(someCredentials['pin']);
} else {
credentials = event;
credentials = someCredentials;
}
getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase);
user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate});
@@ -254,18 +299,16 @@ console.log("THE BROWSER IS OFFLINE");
deferredResult.addMethod(user, 'login');
deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount');
deferredResult.addMethod(this, 'setUser', user);
// deferredResult.addMethod(this, 'setupApplication');
deferredResult.addMethod(this, 'runApplication');
deferredResult.addMethod(this.overlay(), 'done', "", 1);
deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', someCredentials));
deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) {
if (anError['isPermanent'] != true) {
this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()});
this.pages()['loginPage'].setInitialFocus();
}
return anError;
}, this, event))
}, this, someCredentials))
deferredResult.callback();
return deferredResult;
@@ -273,19 +316,19 @@ console.log("THE BROWSER IS OFFLINE");
//-------------------------------------------------------------------------
registerNewUser: function (credentials) {
registerNewUser_handler: function (credentials) {
var deferredResult;
this.overlay().show("creating user");
this.pages()['registrationPage'].setProps({disabled:true});
deferredResult = new Clipperz.Async.Deferred('MainController.registerNewUser', {trace:false});
deferredResult = new Clipperz.Async.Deferred('MainController.registerNewUser', {trace:true});
deferredResult.addCallback(Clipperz.PM.DataModel.User.registerNewAccount,
credentials['username'],
MochiKit.Base.partial(MochiKit.Async.succeed, credentials['passphrase'])
);
deferredResult.addMethod(this, 'doLogin', credentials);
deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', credentials));
deferredResult.addErrback(MochiKit.Base.bind(function (anError) {
if (anError['isPermanent'] != true) {
this.pages()['registrationPage'].setProps({disabled:false});
@@ -307,109 +350,222 @@ console.log("THE BROWSER IS OFFLINE");
},
setUser: function (aUser) {
console.log("SET USER", aUser);
this._user = aUser;
return this._user;
},
//=========================================================================
allCardInfo: function () {
var deferredResult;
var cardInfo;
cardInfo = {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
'_searchableContent': MochiKit.Base.methodcaller('searchableContent'),
'label': MochiKit.Base.methodcaller('label'),
'favicon': MochiKit.Base.methodcaller('favicon')
};
deferredResult = new Clipperz.Async.Deferred('MainController.allCardInfo', {trace:false});
deferredResult.addMethod(this.user(), 'getRecords');
deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false}));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.callback();
return deferredResult;
},
filterCards: function (someCardInfo) {
var filter;
var filterRegExp;
var result;
filter = this.filter().replace(/[^A-Za-z0-9]/g, "\\$&");
filterRegExp = new RegExp(filter, "i");
result = MochiKit.Base.filter(function (aCardInfo) { return filterRegExp.test(aCardInfo['_searchableContent'])}, someCardInfo);
return result;
},
sortCards: function (someCardInfo) {
return someCardInfo.sort(Clipperz.Base.caseInsensitiveKeyComparator('label'));
},
showRecordList: function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.showRecordList', {trace:false});
deferredResult.addMethod(this, 'allCardInfo');
deferredResult.addMethod(this, 'filterCards');
deferredResult.addMethod(this, 'sortCards');
deferredResult.addCallback(MochiKit.Base.bind(function (someRecordInfo) {
this.pages()['cardListPage'].setProps({cardList: someRecordInfo});
}, this));
deferredResult.callback();
return deferredResult;
},
filter: function () {
return this._filter;
},
setFilter: function (aValue) {
this._filter = aValue;
setFilter: function (aType, aValue) {
this._filter = {'type':aType, 'value':aValue};
return this._filter;
},
searchCards: function (someParameters) {
//console.log("SEARCH CARDS", someParameters);
this.setFilter(someParameters);
this.showRecordList();
//----------------------------------------------------------------------------
collectFieldInfo: function (aField) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.collectFieldInfo', {trace:false});
deferredResult.addMethod(aField, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(aField, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(aField, 'value');
deferredResult.setValue('value');
deferredResult.addMethod(aField, 'actionType');
deferredResult.setValue('actionType');
deferredResult.addMethod(aField, 'isHidden');
deferredResult.setValue('isHidden');
deferredResult.values();
deferredResult.callback();
return deferredResult;
},
//=========================================================================
collectDirectLoginInfo: function (aDirectLogin) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.collectDirectLoginInfo', {trace:false});
deferredResult.addMethod(aDirectLogin, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(aDirectLogin, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(aDirectLogin, 'favicon');
deferredResult.setValue('favicon');
deferredResult.values();
runApplication: function () {
MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack'));
/// TODO: remove this TEST HACK
this.moveInPage(this.currentPage(), 'cardListPage');
return this.showRecordList();
deferredResult.callback();
return deferredResult;
},
collectRecordInfo: function (aRecord) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.collectRecordInfo', {trace:false});
deferredResult.addMethod(aRecord, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(aRecord, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(aRecord, 'notes');
deferredResult.setValue('notes');
deferredResult.addMethod(aRecord, 'tags');
deferredResult.setValue('tags');
// this.moveInPage(this.currentPage(), 'preferencePage');
deferredResult.addMethod(aRecord, 'fields');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectFieldInfo'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.setValue('fields');
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.values();
deferredResult.callback();
return deferredResult;
},
showRecord: function (aRecordReference) {
//console.log("Show Record", aRecordReference);
var deferredResult;
updateSelectedCard: function (someInfo) {
var deferredResult;
this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false});
deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
deferredResult.addMethodcaller('content');
deferredResult.addCallback(MochiKit.Base.bind(function (aCard) {
//console.log("CARD DETAILS", aCard);
this.pages()['cardDetailPage'].setProps({card: aCard});
this.pages()['cardListPage'].setProps({selectedCard: null});
if (someInfo == null) {
this.setPageProperties('mainPage', 'selectedCard', {});
deferredResult = MochiKit.Async.succeed();
} else {
this.setPageProperties('mainPage', 'selectedCard', {'loading':true, 'label':someInfo['label'], '_reference':someInfo['reference']});
deferredResult = new Clipperz.Async.Deferred('MainController.updateSelectedCard', {trace:false});
deferredResult.addMethod(this.user(), 'getRecord', someInfo['reference']);
deferredResult.addMethod(this, 'collectRecordInfo');
//console.log("MEDIA QUERY STYLE", this.mediaQueryStyle());
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'selectedCard');
if (this.mediaQueryStyle() == 'narrow') {
deferredResult.addMethod(this, 'setPageProperties', 'cardDetailPage', 'selectedCard');
deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'cardDetailPage');
// deferredResult.addCallback(function (aValue) { console.log("SHOULD SLIDE IN PAGE DETAIL"); return aValue; });
//console.log("SHOULD SLIDE IN PAGE DETAIL");
}
MochiKit.Async.callLater(0.1, MochiKit.Base.method(deferredResult, 'callback'));
}
return deferredResult;
},
//............................................................................
regExpFilterGenerator: function (aRegExp, aSearchField) {
var searchField = aSearchField ? aSearchField : Clipperz.PM.DataModel.Record.defaultSearchField;
return function (aCardInfo) {
aRegExp.lastIndex = 0;
return aRegExp.test(aCardInfo[searchField]);
}
},
selectedCardReference: function () {
return this.pages()['mainPage'].props &&
this.pages()['mainPage'].props['selectedCard'] &&
this.pages()['mainPage'].props['selectedCard'] &&
this.pages()['mainPage'].props['selectedCard']['_reference']
? this.pages()['mainPage'].props['selectedCard']['_reference']
: '';
},
isSelectedCardStillVisible: function (someCards) {
var result;
var reference;
reference = this.selectedCardReference();
result = MochiKit.Iter.some(someCards, function (aCardInfo) {
return aCardInfo['_reference'] == reference;
});
return result;
},
updateSelectedCards: function (shouldIncludeArchivedCards, aFilter) {
var deferredResult;
var sortCriteria;
sortCriteria = Clipperz.Base.caseInsensitiveKeyComparator('label');
deferredResult = new Clipperz.Async.Deferred('MainController.setFilter', {trace:false});
deferredResult.addMethod(this.user(), 'getRecordsInfo', Clipperz.PM.DataModel.Record.defaultCardInfo, shouldIncludeArchivedCards);
if (aFilter['type'] == 'ALL') {
deferredResult.addMethodcaller('sort', sortCriteria);
} else if (aFilter['type'] == 'RECENT') {
deferredResult.addMethodcaller('sort', Clipperz.Base.reverseComparator(MochiKit.Base.keyComparator('accessDate')));
deferredResult.addCallback(function (someCards) { return someCards.slice(0, 9)});
} else if (aFilter['type'] == 'SEARCH') {
deferredResult.addCallback(MochiKit.Base.filter, this.regExpFilterGenerator(Clipperz.PM.DataModel.Record.regExpForSearch(aFilter['value'])));
deferredResult.addMethodcaller('sort', sortCriteria);
} else if (aFilter['type'] == 'TAG') {
deferredResult.addCallback(MochiKit.Base.filter, this.regExpFilterGenerator(Clipperz.PM.DataModel.Record.regExpForTag(aFilter['value'])));
deferredResult.addMethodcaller('sort', sortCriteria);
}
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'cards');
deferredResult.addCallback(MochiKit.Base.bind(function (someCards) {
if (!this.isSelectedCardStillVisible(someCards)) {
this.updateSelectedCard(null);
};
}, this));
deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'cardDetailPage', true);
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'filter', this.filter());
deferredResult.callback();
return deferredResult;
},
//----------------------------------------------------------------------------
setPageProperties: function (aPageName, aKey, aValue) {
var props = {};
props[aKey] = aValue;
this.pages()[aPageName].setProps(props);
return aValue;
},
renderAccountData: function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.renderAccountData', {trace:false});
deferredResult.addMethod(this, 'setFilter', 'ALL');
deferredResult.addMethod(this, 'updateSelectedCards', false);
deferredResult.addMethod(this.user(), 'getTags');
deferredResult.addMethodcaller('sort', Clipperz.Base.caseInsensitiveCompare);
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'tags');
deferredResult.callback();
return deferredResult;
},
runDirectLogin: function (someParameters) {
//=========================================================================
runApplication: function (anUser) {
this.moveInPage(this.currentPage(), 'mainPage');
return this.renderAccountData();
},
/*
runDirectLogin_handler: function (someParameters) {
//console.log("RUN DIRECT LOGIN", someParameters);
var deferredResult;
@@ -427,10 +583,18 @@ console.log("THE BROWSER IS OFFLINE");
anEvent.preventDefault();
anEvent.stopPropagation();
},
*/
//=========================================================================
showPreferences: function (anEvent) {
/*
searchCards_handler: function (someParameters) {
//console.log("SEARCH CARDS", someParameters);
this.setFilter(someParameters);
this.renderAccountData();
},
*/
//=========================================================================
/*
showPreferences_handler: function (anEvent) {
var deferredResult;
this.pages()['preferencePage'].setProps({});
@@ -440,7 +604,7 @@ console.log("THE BROWSER IS OFFLINE");
return deferredResult;
},
*/
//=========================================================================
genericErrorHandler: function (anEvent, anError) {
@@ -471,39 +635,35 @@ console.log("THE BROWSER IS OFFLINE");
slidePage: function (fromPage, toPage, direction) {
var fromPosition;
var toPosition;
var toPosition;
var itemToTransition;
if (direction == "LEFT") {
fromPosition = 'right';
toPosition = 'left'
toPosition = 'left';
itemToTransition = toPage;
} else {
fromPosition = 'left';
toPosition = 'right'
toPosition = 'right';
itemToTransition = fromPage;
}
MochiKit.DOM.addElementClass(fromPage, toPosition + ' transition');
MochiKit.DOM.addElementClass(itemToTransition, 'transition');
MochiKit.DOM.addElementClass(toPage, fromPosition);
MochiKit.DOM.removeElementClass(toPage, toPosition);
MochiKit.DOM.addElementClass(toPage, 'transition');
MochiKit.Async.callLater(0.1, function () {
MochiKit.Async.callLater(0, function () {
MochiKit.DOM.addElementClass(fromPage, toPosition);
MochiKit.DOM.removeElementClass(toPage, fromPosition);
})
MochiKit.Async.callLater(0.5, function () {
MochiKit.DOM.removeElementClass(fromPage, 'transition');
MochiKit.DOM.removeElementClass(toPage, 'transition');
MochiKit.DOM.removeElementClass(itemToTransition, 'transition');
})
},
rotateInPage: function (fromPage, toPage) {
// Broken! :(
MochiKit.DOM.addElementClass(MochiKit.DOM.getElement('mainDiv'), 'show-right');
},
//.........................................................................
goBack: function () {
goBack_handler: function () {
var fromPage;
var toPage;
@@ -525,6 +685,7 @@ console.log("THE BROWSER IS OFFLINE");
setCurrentPage: function (aPage) {
this.pageStack().unshift(aPage);
this.refreshCurrentPage();
},
moveInPage: function (fromPage, toPage, addToHistory) {
@@ -552,12 +713,108 @@ console.log("THE BROWSER IS OFFLINE");
this.setCurrentPage(toPage);
},
//=========================================================================
//-------------------------------------------------------------------------
synchronizeLocalData: function (anEvent) {
messageBoxContent: function () {
var message;
var level;
message = "";
level = 'HIDE';
//console.log("messageBox - this.user()", this.user());
if (this.user() != null && this.user().accountInfo() != null && this.user().accountInfo().featureSet() == 'EXPIRED') {
message = "Exprired subscription";
level = 'ERROR';
}
return {
'message': message,
'level': level
};
},
userAccountInfo: function () {
var result;
result = {};
if (this.user() != null) {
var usefulFields = [
'currentSubscriptionType',
'expirationDate',
'featureSet',
'isExpired',
'isExpiring',
'paymentVerificationPending'
];
var attributes = this.user().accountInfo()._attributes;
MochiKit.Iter.forEach(usefulFields, function (aFieldName) {
result[aFieldName] = attributes[aFieldName];
})
};
return result;
},
genericPageProperties: function () {
return {
'style': this.mediaQueryStyle(),
'isTouchDevice': this.isTouchDevice(),
'isDesktop': this.isDesktop(),
'hasKeyboard': this.hasKeyboard()
};
},
pageProperties: function (aPageName) {
var result;
var extraProperties = null;
result = this.genericPageProperties();
if (aPageName == 'loginPage') {
extraProperties = {
'mode': 'CREDENTIALS',
'isNewUserRegistrationAvailable': true,
'disabled': false,
};
} else if (aPageName == 'registrationPage') {
} else if (aPageName == 'mainPage') {
extraProperties = {
'messageBox': this.messageBoxContent(),
'accountStatus': this.userAccountInfo(),
'selectionPanelStatus': this.isSelectionPanelOpen() ? 'OPEN' : 'CLOSED',
'settingsPanelStatus': this.isSettingsPanelOpen() ? 'OPEN' : 'CLOSED',
// 'cards': …,
// 'tags': …,
// 'selectedCard': …,
};
} else if (aPageName == 'errorPage') {
extraProperties = {
'message': ''
};
}
if (extraProperties != null) {
result = MochiKit.Base.update(result, extraProperties);
}
//console.log("MainController.pageProperties", result);
return result;
},
refreshCurrentPage: function () {
if (this.pages()[this.currentPage()] != null) {
this.pages()[this.currentPage()].setProps(this.pageProperties(this.currentPage()));
}
},
//=========================================================================
/*
synchronizeLocalData_handler: function (anEvent) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('MainController.synchronizeLocalData', {trace:true});
deferredResult = new Clipperz.Async.Deferred('MainController.synchronizeLocalData', {trace:false});
// deferredResult.addMethod(this.proxy(), 'message', 'downloadAccountData', {});
deferredResult.addMethod(this.user().connection(), 'message', 'downloadAccountData', {});
deferredResult.addCallback(function (aResult) {
@@ -570,8 +827,126 @@ console.log("THE BROWSER IS OFFLINE");
return deferredResult;
},
*/
//=========================================================================
resetPanels: function () {
this._isSelectionPanelOpen = false;
this._isSettingsPanelOpen = false;
},
isSelectionPanelOpen: function () {
return this._isSelectionPanelOpen;
},
toggleSelectionPanel_handler: function (anEvent) {
this._isSelectionPanelOpen = !this._isSelectionPanelOpen;
this.setCloseMaskAction(MochiKit.Base.method(this, 'toggleSelectionPanel_handler'));
this.refreshCurrentPage();
},
isSettingsPanelOpen: function () {
return this._isSettingsPanelOpen;
},
toggleSettingsPanel_handler: function (anEvent) {
this._isSettingsPanelOpen = !this._isSettingsPanelOpen;
this.setCloseMaskAction(MochiKit.Base.method(this, 'toggleSettingsPanel_handler'));
this.refreshCurrentPage();
},
cardSelected_handler: function (aReference) {
this.updateSelectedCard(aReference);
},
//----------------------------------------------------------------------------
addCardClick_handler: function () {
console.log("ADD CARD CLICK");
},
deleteCard_handler: function (anEvent) {
console.log("DELETE CARD", anEvent['reference']);
},
archiveCard_handler: function (anEvent) {
console.log("ARCHIVE CARD", anEvent['reference']);
},
editCard_handler: function (anEvent) {
console.log("EDIT CARD", anEvent['reference']);
},
goBackToMainPage_handler: function (anEvent) {
this.updateSelectedCard();
this.moveOutPage(this.currentPage(), 'mainPage');
},
//============================================================================
selectAllCards_handler: function () {
this.setFilter('ALL');
this.updateSelectedCards(false, this.filter());
},
selectRecentCards_handler: function () {
this.setFilter('RECENT');
this.updateSelectedCards(false, this.filter());
},
tagSelected_handler: function (aTag) {
this.setFilter('TAG', aTag);
this.updateSelectedCards(false, this.filter());
},
//----------------------------------------------------------------------------
setCloseMaskAction: function (aFunction) {
this._closeMaskAction = aFunction;
},
maskClick_handler: function () {
this._closeMaskAction.apply(this);
this._closeMaskAction = null;
},
//============================================================================
matchMediaQuery_handler: function (newQueryStyle) {
this._mediaQueryStyle = newQueryStyle;
if (this.currentPage() == 'cardDetailPage') {
this.moveOutPage(this.currentPage(), 'mainPage');
}
this.resetPanels();
this.refreshCurrentPage();
},
unmatchMediaQuery_handler: function (queryStyle) {
},
mediaQueryStyle: function () {
return this._mediaQueryStyle;
},
//----------------------------------------------------------------------------
isTouchDevice: function () {
return this._isTouchDevice;
},
isDesktop: function () {
return this._isDesktop;
},
hasKeyboard: function () {
return this._hasKeyboard;
},
//============================================================================
/*
wrongAppVersion: function (anError) {
// this.pages()['errorPage'].setProps({message:anError.message});