1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-29 10:27: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

@@ -21,11 +21,8 @@ refer to http://www.clipperz.com.
*/
//Clipperz.Async = MochiKit.Async;
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.Async) == 'undefined') { Clipperz.Async = {}; }
"use strict";
Clipperz.Base.module('Clipperz.Async');
Clipperz.Async.VERSION = "0.1";
Clipperz.Async.NAME = "Clipperz.Async";
@@ -276,6 +273,12 @@ Clipperz.Base.extend(Clipperz.Async.Deferred, MochiKit.Async.Deferred, {
}, this));
},
'values': function () {
this.addCallback(MochiKit.Base.bind(function () {
return this.vars();
}, this));
},
//=============================================================================
'wait': function (someSeconds) {
@@ -702,6 +705,6 @@ MochiKit.Base.update(Clipperz.Async, {
//#############################################################################
CLIPPERZ_DEFERRED_LOGGING_ENABLED = true;
CLIPPERZ_DEFERRED_TRACING_ENABLED = false;
CLIPPERZ_DEFERRED_CALL_LOGGING_ENABLED = false;
var CLIPPERZ_DEFERRED_LOGGING_ENABLED = true;
var CLIPPERZ_DEFERRED_TRACING_ENABLED = false;
var CLIPPERZ_DEFERRED_CALL_LOGGING_ENABLED = false;

View File

@@ -21,6 +21,8 @@ refer to http://www.clipperz.com.
*/
"use strict";
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.Base) == 'undefined') { Clipperz.Base = {}; }
@@ -189,6 +191,10 @@ MochiKit.Base.update(Clipperz.Base, {
return Clipperz.Base.removeObjectFromArray(anObject, anArray);
},
'arrayWithUniqueValues': function (anArray) {
return anArray.filter(function (value, index, self) { return self.indexOf(value) === index; });
},
//-------------------------------------------------------------------------
'splitStringAtFixedTokenSize': function(aString, aTokenSize) {

View File

@@ -21,6 +21,7 @@ refer to http://www.clipperz.com.
*/
"use strict";
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
//=============================================================================

View File

@@ -21,7 +21,8 @@ refer to http://www.clipperz.com.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
"use strict";
Clipperz.Base.module('Clipperz');
Clipperz.CSVProcessor = function(args) {
@@ -32,9 +33,9 @@ Clipperz.CSVProcessor = function(args) {
// this._string = undefined;
// this._fields = undefined;
this._quoteChar = args['quoteChar'] || "\042";
this._quoteChar = args['quoteChar'] || "\0x42";
this._eol = args['eol'] || "";
this._escapeChar = args['escapeChar'] || "\042";
this._escapeChar = args['escapeChar'] || "\0x42";
this._separatorChar = args['separatorChar'] || ",";
this._binary = args['binary'] || false;
this._alwaysQuote = args['alwaysQuote'] || false;

View File

@@ -293,7 +293,7 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new
Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource = function(args) {
args = args || {};
this._intervalTime = args.intervalTime || 1000;
this._intervalTime = args.intervalTime || 500;
this._browserCrypto = args.browserCrypto;
Clipperz.Crypto.PRNG.RandomnessSource.call(this, args);
@@ -318,7 +318,7 @@ Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource.prototype = MochiKit.Base.upda
var bytesToCollect;
if (this.boostMode() == true) {
bytesToCollect = 64;
bytesToCollect = 256;
} else {
bytesToCollect = 8;
}

View File

@@ -56,7 +56,7 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, {
return Clipperz.Crypto.SRP._n;
},
//-------------------------------------------------------------------------
//.........................................................................
'g': function() {
if (Clipperz.Crypto.SRP._g == null) {
@@ -66,6 +66,8 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, {
return Clipperz.Crypto.SRP._g;
},
//.........................................................................
'k': function() {
if (Clipperz.Crypto.SRP._k == null) {
// Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt(this.stringHash(this.n().asString() + this.g().asString()), 16);

View File

@@ -21,8 +21,8 @@ refer to http://www.clipperz.com.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.DOM) == 'undefined') { Clipperz.DOM = {}; }
"use strict";
Clipperz.Base.module('Clipperz.DOM');
Clipperz.DOM.VERSION = "0.1";
Clipperz.DOM.NAME = "Clipperz.DOM";

View File

@@ -21,8 +21,8 @@ refer to http://www.clipperz.com.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.Date) == 'undefined') { Clipperz.Date = {}; }
"use strict";
Clipperz.Base.module('Clipperz.Date');
Clipperz.Date.VERSION = "0.1";
Clipperz.Date.NAME = "Clipperz.Date";

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});

View File

@@ -0,0 +1,261 @@
/*
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/.
*/
/*
* onMediaQuery
* http://springload.co.nz/love-the-web/
*
* Copyright 2012, Springload
* Released under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Date: Fri 24 October, 2012
*/
;(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(function () {
// Also create a global in case some scripts
// that are loaded still are looking for
// a global even when an AMD loader is in use.
return (root.MQ = factory(root, root.MQ || {}));
});
} else {
// Browser globals
root.MQ = factory(root, root.MQ || {});
}
}(this, function(mq) {
/**
* Initialises the MQ object and sets the initial media query callbacks
* @returns Void(0)
*/
mq.init = function(query_array) {
// Container for all callbacks registered with the plugin
this.callbacks = [];
this.context = ''; //current active query
this.new_context = ''; //current active query to be read inside callbacks, as this.context won't be set when they're called!
if (typeof(query_array) !== 'undefined' ) {
for (i = 0; i < query_array.length; i++) {
var r = this.addQuery(query_array[i]);
}
}
// Add a listener to the window.resize event, pass mq/self as the scope.
this.addEvent(window, 'resize', mq.listenForChange, mq);
// Figure out which query is active on load.
this.listenForChange();
};
/**
* Binds to the window.onResize and checks for media query changes
* @returns Void(0)
*/
mq.listenForChange = function() {
var query_string;
// Get the value of html { font-family } from the element style.
if (document.documentElement.currentStyle) {
query_string = document.documentElement.currentStyle["fontFamily"];
}
if (window.getComputedStyle) {
query_string = window.getComputedStyle(document.documentElement,null).getPropertyValue('font-family');
}
// No support for CSS enumeration? Return and avoid errors.
if (query_string === null) return;
// Android browsers place a "," after an item in the font family list.
// Most browsers either single or double quote the string.
query_string = query_string.replace(/['",]/g, '');
if (query_string !== this.context) {
this.new_context = query_string;
this.triggerCallbacks(this.context, 'unmatch');
this.triggerCallbacks(this.new_context, 'match');
}
this.context = this.new_context;
};
/**
* Attach a new query to test.
* @param query_object {
* context: ['some_media_query','some_other_media_query'],
* call_for_each_context: true,
* callback: function() {
* //something awesome
* }
* }
* @returns A reference to the query_object that was added
*/
mq.addQuery = function(query_object) {
if (query_object === null || query_object === undefined) return;
this.callbacks.push(query_object);
// If the context is passed as a string, turn it into an array (for unified approach elsewhere in the code)
if (typeof(query_object.context) == "string") {
query_object.context = [query_object.context];
}
// See if "call_for_each_context" is set, if not, set a default (for unified approach elsewhere in the code)
if (typeof(query_object.call_for_each_context) !== "boolean") {
query_object.call_for_each_context = true; // Default
}
// Fire the added callback if it matches the current context
if (this.context !== '' && this._inArray(this.context, query_object.context)) {
query_object.match();
}
return this.callbacks[ this.callbacks.length - 1];
};
/**
* Remove a query_object by reference.
* @returns Void(0)
*/
mq.removeQuery = function(query_object) {
if (query_object === null || query_object === undefined) return;
var match = -1;
while ((match = mq._indexOf(query_object,this.callbacks)) > -1) {
this.callbacks.splice(match, 1);
}
};
/**
* Loop through the stored callbacks and execute
* the ones that are bound to the current context.
* @returns Void(0)
*/
mq.triggerCallbacks = function(size, key) {
var i, callback_function, call_for_each_context;
for (i = 0; i < this.callbacks.length; i++) {
// Don't call for each context?
if(this.callbacks[i].call_for_each_context === false) {
if ((key === 'match' && this._inArray(this.context, this.callbacks[i].context)) ||
(key === 'unmatch' && this._inArray(this.new_context, this.callbacks[i].context))) {
// Was previously called, and we don't want to call it for each context
continue;
}
}
callback_function = this.callbacks[i][key];
if (this._inArray(size, this.callbacks[i].context) && callback_function !== undefined) {
callback_function();
}
}
};
/**
* Swiss Army Knife event binding, in lieu of jQuery.
* @returns Void(0)
*/
mq.addEvent = function(elem, type, eventHandle, eventContext) {
if (elem === null || elem === undefined) return;
// If the browser supports event listeners, use them.
if (elem.addEventListener) {
elem.addEventListener(type, function() { eventHandle.call(eventContext); }, false);
} else if (elem.attachEvent ) {
elem.attachEvent("on" + type, function() { eventHandle.call(eventContext); });
// Otherwise, replace the current thing bound to on[whatever]! Consider refactoring.
} else {
elem["on" + type] = function() { eventHandle.call(eventContext); };
}
};
/**
* Function to return the mediaquery's previous context
* @returns String returns the current mediaquery's context
*/
mq.getPreviousContext = function()
{
return this.context;
};
/**
* Function to return the mediaquery's current context
* @returns String returns the current mediaquery's context
*/
mq.getContext = function()
{
return this.new_context;
};
/**
* Internal helper function that checks wether "needle" occurs in "haystack"
* @param needle Mixed Value to look for in haystack array
* @param haystack Array Haystack array to search in
* @returns Boolan True if the needle occurs, false otherwise
*/
mq._inArray = function(needle, haystack)
{
var length = haystack.length;
for(var i = 0; i < length; i++) {
if(haystack[i] == needle) return true;
}
return false;
};
/**
* IE8 do not supports Array.properties.indexOf
* copy from jQuery.
* in lieu of jQuery.
* @returns int
*/
mq._indexOf = function( elem, arr, i )
{
var len;
if ( arr ) {
if ( arr.indexOf ) {
return arr.indexOf( elem, i );
}
len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
}
return -1;
}
// Expose the functions.
return mq;
}));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -44,12 +44,27 @@ MochiKit.Logging.logError("## MainController - GENERIC ERROR" + "\n" + "==>> " +
React.initializeTouchEvents(true);
Clipperz.PM.RunTime = {};
function run() {
function initOnMediaQuery () {
MQ.init(MochiKit.Base.map(function (queryStyle) {
return {
context: queryStyle,
match: function () {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'matchMediaQuery', queryStyle);
},
unmatch: function () {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unmatchMediaQuery', queryStyle);
},
}
// }, ['extra-short', 'narrow', 'wide', 'extra-wide']));
}, Clipperz_PM_UI_availableStyles));
}
function run () {
var parameters = {};
Clipperz.PM.Strings.Languages.initSetup();
if ((window.location.search.indexOf('registration') != -1) || (window.location.hash.indexOf('registration') != -1)) {
parameters['shouldShowRegistrationForm'] = true;
} else {
@@ -59,7 +74,29 @@ function run() {
Clipperz.PM.DataModel.devicePreferences = new Clipperz.PM.DataModel.DevicePreferences({});
Clipperz.PM.RunTime.mainController = new Clipperz.PM.UI.MainController();
initOnMediaQuery();
Clipperz.PM.RunTime.mainController.run(parameters);
}
MochiKit.DOM.addLoadEvent(run);
/* * /
MochiKit.DOM.addLoadEvent(simulateLogin);
function simulateLogin () {
var getURLParameter = function (name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null;
}
Clipperz.Crypto.PRNG.defaultRandomGenerator().fastEntropyAccumulationForTestingPurpose();
// MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', {username:'joe', passphrase:'clipperz'}); // FULL
// http://localhost:8888/delta/index.html?username=joe&passphrase=clipperz
// MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', {username:';', passphrase:';'}); // EXPIRED
// http://localhost:8888/delta/index.html?username=%3B&passphrase=%3B
if ((getURLParameter('u') != null) && (getURLParameter('p'))) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', {username:getURLParameter('u'), passphrase:getURLParameter('p')});
}
}
/ * */