22 files changed, 1493 insertions, 803 deletions
diff --git a/frontend/gamma/js/Clipperz/Async.js b/frontend/gamma/js/Clipperz/Async.js index 97d8ecf..f7a9517 100644 --- a/frontend/gamma/js/Clipperz/Async.js +++ b/frontend/gamma/js/Clipperz/Async.js @@ -500,35 +500,35 @@ MochiKit.Base.update(Clipperz.Async, { deferredResult.callback(aValue); return deferredResult; }, aName, someRequests, args); }, //------------------------------------------------------------------------- 'collectAll': function (someDeferredObjects) { var deferredResult; deferredResult = new MochiKit.Async.DeferredList(someDeferredObjects, false, false, false); deferredResult.addCallback(function (aResultList) { return MochiKit.Base.map(function (aResult) { if (aResult[0]) { return aResult[1]; - } else { + } else { throw aResult[1]; - } + } }, aResultList); }); return deferredResult; }, //------------------------------------------------------------------------- 'setItem': function (anObject, aKey, aValue) { anObject[aKey] = aValue; return anObject; }, 'setItemOnObject': function (aKey, aValue, anObject) { anObject[aKey] = aValue; diff --git a/frontend/gamma/js/Clipperz/Base.js b/frontend/gamma/js/Clipperz/Base.js index 76b2c3f..9d399d9 100644 --- a/frontend/gamma/js/Clipperz/Base.js +++ b/frontend/gamma/js/Clipperz/Base.js @@ -64,32 +64,37 @@ MochiKit.Base.update(Clipperz.Base, { }, 'isEmail': function (aValue) { return (MochiKit.Base.emailRegExp.test(aValue)); }, //------------------------------------------------------------------------- 'caseInsensitiveCompare': function (a, b) { return MochiKit.Base.compare(a.toLowerCase(), b.toLowerCase()); }, 'reverseComparator': function (aComparator) { return MochiKit.Base.compose(function(aResult) { return -aResult; }, aComparator); }, + 'caseInsensitiveKeyComparator': function (aKey) { + return function (a, b) { + return MochiKit.Base.compare(a[aKey].toLowerCase(), b[aKey].toLowerCase()); + } + }, //------------------------------------------------------------------------- /* 'dependsOn': function(module, deps) { if (!(module in Clipperz)) { MochiKit[module] = {}; } if (typeof(dojo) != 'undefined') { dojo.provide('Clipperz.' + module); } for (var i = 0; i < deps.length; i++) { if (typeof(dojo) != 'undefined') { dojo.require('Clipperz.' + deps[i]); } if (typeof(JSAN) != 'undefined') { JSAN.use('Clipperz.' + deps[i], []); diff --git a/frontend/gamma/js/Clipperz/PM/Connection.js b/frontend/gamma/js/Clipperz/PM/Connection.js index b4e8aaa..a05a310 100644 --- a/frontend/gamma/js/Clipperz/PM/Connection.js +++ b/frontend/gamma/js/Clipperz/PM/Connection.js @@ -28,32 +28,33 @@ if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } //----------------------------------------------------------------------------- // // Abstract C O N N E C T I O N class // //----------------------------------------------------------------------------- Clipperz.PM.Connection = function (args) { args = args || {}; this._proxy = args.proxy || Clipperz.PM.Proxy.defaultProxy; this._getCredentialsFunction = args.getCredentialsFunction; this._clipperz_pm_crypto_version = null; this._connectionId = null; this._sharedSecret = null; + this._serverLockValue = null; return this; } Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, { 'toString': function() { return "Connection [" + this.version() + "]"; }, //========================================================================= 'version': function() { throw Clipperz.Base.exception.AbstractMethod; }, @@ -133,32 +134,42 @@ MochiKit.Logging.logError("### Connection.defaultErrorHandler: " + anErrorString }, 'setSharedSecret': function (aValue) { this._sharedSecret = aValue; }, //------------------------------------------------------------------------- 'connectionId': function() { return this._connectionId; }, 'setConnectionId': function(aValue) { this._connectionId = aValue; }, + //------------------------------------------------------------------------- + + 'serverLockValue': function () { + return this._serverLockValue; + }, + + 'setServerLockValue': function (aValue) { + this._serverLockValue = aValue; + }, + //========================================================================= /* // TODO: ????? 'oneTimePassword': function() { return this._oneTimePassword; }, 'setOneTimePassword': function(aValue) { this._oneTimePassword = aValue; }, */ //========================================================================= 'reset': function() { this.setSharedSecret(null); this.setConnectionId(null); @@ -307,39 +318,38 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz. return Clipperz.Async.callbacks("Connction.redeemOTP", [ MochiKit.Base.method(this.proxy(), 'handshake', args), function(aResult) { return Clipperz.PM.Crypto.deferredDecrypt({ value: aResult['data'], key: normalizedOTP, version:aResult['version'] }); }, function(aResult) { return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString(); } ], {trace:false}) }, - 'login': function(/*anUsername, aPassphrase*/) { + 'login': function(isReconnecting) { var deferredResult; var cryptoVersion; var srpConnection; cryptoVersion = this.clipperz_pm_crypto_version(); - deferredResult = new Clipperz.Async.Deferred("Connection.login", {trace:false}); deferredResult.addCallback(this.getCredentialsFunction()); deferredResult.addMethod(this, 'normalizedCredentials'); // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_sendingCredentials'); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); deferredResult.addCallback(MochiKit.Base.bind(function(someCredentials) { srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() }); }, this)); deferredResult.addCallback(function() { var result; result = { message: 'connect', version: cryptoVersion, parameters: { C: srpConnection.C(), @@ -386,84 +396,97 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz. if (someParameters['M2'] == srpConnection.M2()) { result = MochiKit.Async.succeed(someParameters); } else { result = MochiKit.Async.fail(Clipperz.PM.Connection.exception.WrongChecksum); } return result; }); deferredResult.addCallback(MochiKit.Base.bind(function(someParameters) { this.setConnectionId(someParameters['connectionId']); this.setSharedSecret(srpConnection.K()); // TODO: ????? // if (this.oneTimePassword() != null) { /// ?? result = this.user().oneTimePasswordManager().archiveOneTimePassword(this.oneTimePassword())); // } + + if ((isReconnecting == true) && (this.serverLockValue() != someParameters['lock'])) { + throw Clipperz.PM.Connection.exception.StaleData; + } else { + this.setServerLockValue(someParameters['lock']); + } + return someParameters; }, this)); // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_loggedIn'); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); deferredResult.addCallback(MochiKit.Async.succeed, {result:"done"}); deferredResult.callback(); return deferredResult; }, //========================================================================= 'logout': function() { return Clipperz.Async.callbacks("Connection.logout", [ MochiKit.Base.method(this, 'setSharedSecret'), MochiKit.Base.method(this.proxy(), 'logout', {}) ], {trace:false}); }, //========================================================================= 'ping': function () { // TODO: ping the server in order to have a valid session }, //========================================================================= 'message': function(aMessageName, someParameters) { var args; + var parameters; + + parameters = someParameters || {}; + if (typeof(parameters['user']) != 'undefined') { + parameters['user']['lock'] = this.serverLockValue(); + } //console.log(">>> Connection.message", aMessageName, someParameters); args = { message: aMessageName, srpSharedSecret: this.sharedSecret(), - parameters: (someParameters || {}) +// parameters: (someParameters || {}) + parameters: parameters } return this.sendMessage(args); }, //------------------------------------------------------------------------- 'sendMessage': function(someArguments) { var deferredResult; deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false}); deferredResult.addMethod(this.proxy(), 'message', someArguments); deferredResult.addCallback(MochiKit.Base.bind(function(res) { if (typeof(res['lock']) != 'undefined') { -// TODO: ????? -// ?? this.user().setLock(res['lock']); + this.setServerLockValue(res['lock']); } return res; }, this)); deferredResult.addErrback(MochiKit.Base.method(this, 'messageExceptionHandler'), someArguments); deferredResult.callback(); return deferredResult }, //------------------------------------------------------------------------- 'messageExceptionHandler': function(anOriginalMessageArguments, anError) { var result; console.log(">>> Connection.messageExceptionHandler", anError, anError.message); @@ -574,32 +597,33 @@ Clipperz.PM.Connection.SRP['1.1'].prototype = MochiKit.Base.update(new Clipperz. return result; }, //----------------------------------------------------------------------------- 'hash': function() { return Clipperz.PM.Crypto.encryptingFunctions.versions['0.2'].hash; }, //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); Clipperz.PM.Connection.exception = { WrongChecksum: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue"), + StaleData: new MochiKit.Base.NamedError("Stale data"), UnexpectedRequest: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.UnexpectedRequest") }; Clipperz.PM.Connection.communicationProtocol = { 'currentVersion': '0.2', 'versions': { '0.1': Clipperz.PM.Connection.SRP['1.0'], //Clipperz.Crypto.SRP.versions['1.0'].Connection, '0.2': Clipperz.PM.Connection.SRP['1.1'] //Clipperz.Crypto.SRP.versions['1.1'].Connection }, 'fallbackVersions': { // 'current': '0.1', '0.2': '0.1', '0.1': null } }; diff --git a/frontend/gamma/js/Clipperz/PM/DataModel/User.js b/frontend/gamma/js/Clipperz/PM/DataModel/User.js index 72d4006..646ce21 100644 --- a/frontend/gamma/js/Clipperz/PM/DataModel/User.js +++ b/frontend/gamma/js/Clipperz/PM/DataModel/User.js @@ -31,33 +31,33 @@ if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; //############################################################################# Clipperz.PM.DataModel.User = function (args) { args = args || {}; Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments); this._username = args.username || null; this._getPassphraseFunction = args.getPassphraseFunction || null; this._data = null; this._connection = null; this._connectionVersion = 'current'; this._serverData = null; - this._serverLockValue = null; +// this._serverLockValue = null; this._transientState = null; this._deferredLocks = { 'passphrase': new MochiKit.Async.DeferredLock(), 'serverData': new MochiKit.Async.DeferredLock(), // 'recordsIndex': new MochiKit.Async.DeferredLock(), // 'directLoginsIndex': new MochiKit.Async.DeferredLock() // 'preferences': new MochiKit.Async.DeferredLock() // 'oneTimePasswords': new MochiKit.Async.DeferredLock() '__syntaxFix__': 'syntax fix' }; return this; } Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { @@ -80,41 +80,41 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { 'displayName': function() { return "" + this.username() + ""; }, //------------------------------------------------------------------------- 'data': function () { if (this._data == null) { this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/); }; return this._data; }, //------------------------------------------------------------------------- - +/* 'serverLockValue': function () { return this._serverLockValue; }, 'setServerLockValue': function (aValue) { this._serverLockValue = aValue; }, - +*/ //------------------------------------------------------------------------- 'transientState': function () { if (this._transientState == null) { this._transientState = {} } return this._transientState; }, 'resetTransientState': function (isCommitting) { this._transientState = null; }, //------------------------------------------------------------------------- @@ -207,60 +207,60 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { // this._serverLockValue = Clipperz.PM.Crypto.randomKey(); }, //......................................................................... 'registerAsNewAccount': function () { var deferredResult; deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false}); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3}); deferredResult.addMethod(this, 'initialSetupWithNoData') deferredResult.addMethod(this, 'getPassphrase'); deferredResult.addMethod(this, 'prepareRemoteDataWithKey'); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); deferredResult.addMethod(this.connection(), 'register'); - deferredResult.addCallback(MochiKit.Base.itemgetter('lock')); - deferredResult.addMethod(this, 'setServerLockValue'); +// deferredResult.addCallback(MochiKit.Base.itemgetter('lock')); +// deferredResult.addMethod(this, 'setServerLockValue'); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered'); // deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure')); deferredResult.callback(); return deferredResult; }, //------------------------------------------------------------------------- 'login': function () { 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'); + deferredResult.addMethod(this.connection(), 'login', false); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn'); deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback')); deferredResult.callback(); return deferredResult; }, //......................................................................... 'handleConnectionFallback': function(aValue) { var result; if (aValue instanceof MochiKit.Async.CancelledError) { result = aValue; } else { @@ -316,33 +316,33 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { } return result; }, //------------------------------------------------------------------------- 'unpackServerData': function (someServerData) { var unpackedData; var headerVersion; var recordsIndex; var preferences; var oneTimePasswords; //console.log(">>> ***************** user.unpackServerData", someServerData); - this.setServerLockValue(someServerData['lock']); +// this.setServerLockValue(someServerData['lock']); headerVersion = this.headerFormatVersion(someServerData['header']); switch (headerVersion) { case 'LEGACY': var legacyHeader; legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'), 'remoteData': { 'data': someServerData['header'], 'version': someServerData['version'], 'recordsStats': someServerData['recordsStats'] }, // 'encryptedDataKeypath': 'data', // 'encryptedVersionKeypath': 'version', @@ -649,34 +649,34 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { deferredResult.callback(); // recordsIndex = legacyHeader; // preferences = legacyHeader; // oneTimePasswords = legacyHeader; return deferredResult; }, //========================================================================= 'commitTransientState': function () { return Clipperz.Async.callbacks("User.commitTransientState", [ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'), MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'), MochiKit.Base.method(this, 'transientState'), - MochiKit.Base.itemgetter('lock'), - MochiKit.Base.method(this, 'setServerLockValue'), +// MochiKit.Base.itemgetter('lock'), +// MochiKit.Base.method(this, 'setServerLockValue'), MochiKit.Base.method(this, 'resetTransientState', true) ], {trace:false}); }, //------------------------------------------------------------------------- 'revertChanges': function () { return Clipperz.Async.callbacks("User.revertChanges", [ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'), MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'), MochiKit.Base.method(this, 'resetTransientState', false) ], {trace:false}); }, //========================================================================= @@ -727,33 +727,33 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) { var header; header = {}; header['records'] = someHeaderPackedData['recordIndex']['records']; header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins']; header['preferences'] = {'data': someHeaderPackedData['preferences']['data']}; // this._serverData['header']['preferences']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['preferences']; // ??????????? header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']}; // this._serverData['header']['oneTimePasswords']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['oneTimePasswords']; // ??????????? header['version'] = '0.1'; aResult['header'] = Clipperz.Base.serializeJSON(header); aResult['statistics'] = this._serverData['statistics']; // "someHeaderPackedData['statistics']['data']"; return aResult; }, this), result); deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion); - deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue()); +// deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue()); deferredResult.callback(); return deferredResult; }, //========================================================================= 'saveChanges': function () { var deferredResult; var messageParameters; messageParameters = {}; deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false}); deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex'); diff --git a/frontend/gamma/js/Clipperz/PM/PIN.js b/frontend/gamma/js/Clipperz/PM/PIN.js new file mode 100644 index 0000000..bc932b2 --- a/dev/null +++ b/frontend/gamma/js/Clipperz/PM/PIN.js @@ -0,0 +1,134 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz Community Edition. +Clipperz Community Edition is an online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see + <http://www.gnu.org/licenses/>. + +*/ + +if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } +if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } +if (typeof(Clipperz.PM.PIN) == 'undefined') { Clipperz.PM.PIN = {}; } + +MochiKit.Base.update(Clipperz.PM.PIN, { + + //------------------------------------------------------------------------- + + '__repr__': function () { + return "[" + this.NAME + " " + this.VERSION + "]"; + }, + + //------------------------------------------------------------------------- + + 'toString': function () { + return this.__repr__(); + }, + + 'CREDENTIALS': 'CLIPPERZ.CREDENTIALS', + 'FAILURE_COUNT': 'CLIPPERZ.FAILED_LOGIN_COUNT', + 'ALLOWED_RETRY': 3, + + //------------------------------------------------------------------------- + + 'isSet': function () { + return (this.storedCredentials() != null); + }, + + 'storedCredentials': function () { + return localStorage[this.CREDENTIALS]; + }, + + //------------------------------------------------------------------------- + + 'recordFailedAttempt': function () { + var failureCount; + var result; + + failureCount = localStorage[this.FAILURE_COUNT]; + + if (failureCount == null) { + failureCount = 0 + } + + failureCount ++; + + if (failureCount < this.ALLOWED_RETRY) { + localStorage[this.FAILURE_COUNT] = failureCount; + result = failureCount; + } else { + this.removeLocalCredentials(); + result = -1; + } + + return result; + }, + + 'resetFailedAttemptCount': function () { + localStorage.removeItem(this.FAILURE_COUNT); + }, + + 'failureCount': function () { + return localStorage[this.FAILURE_COUNT]; + }, + + //------------------------------------------------------------------------- + + 'deriveKeyFromPin': function (aPIN) { + return Clipperz.Crypto.SHA.sha256(new Clipperz.ByteArray(aPIN)); + }, + + 'credentialsWithPIN': function (aPIN) { + var byteArrayValue; + var decryptedValue; + var result; + + byteArrayValue = (new Clipperz.ByteArray()).appendBase64String(localStorage[this.CREDENTIALS]); + decryptedValue = Clipperz.Crypto.AES.decrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).asString(); + try { + result = Clipperz.Base.evalJSON(decryptedValue); + } catch (error) { + result = {'username':'fakeusername', 'passphrase':'fakepassphrase'}; + } + + return result; + }, + + 'setCredentialsWithPIN': function (aPIN, someCredentials) { + var encodedValue; + var byteArrayValue; + var encryptedValue; + + encodedValue = Clipperz.Base.serializeJSON(someCredentials); + byteArrayValue = new Clipperz.ByteArray(encodedValue); + encryptedValue = Clipperz.Crypto.AES.encrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).toBase64String(); + + localStorage[this.CREDENTIALS] = encryptedValue; + }, + + 'removeLocalCredentials': function () { + localStorage.removeItem(this.CREDENTIALS); + localStorage.removeItem(this.FAILURE_COUNT); + }, + + //------------------------------------------------------------------------- + __syntaxFix__: "syntax fix" + +}); + diff --git a/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js new file mode 100644 index 0000000..803c590 --- a/dev/null +++ b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js @@ -0,0 +1,65 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz Community Edition. +Clipperz Community Edition is an online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see + <http://www.gnu.org/licenses/>. + +*/ + +if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } +if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } + +//============================================================================= + +Clipperz.PM.Proxy.OfflineCache = function(args) { + args = args || {}; + + Clipperz.PM.Proxy.Offline.superclass.constructor.call(this, args); + +// this._dataStore = args.dataStore || new Clipperz.PM.Proxy.Offline.DataStore(args); + + return this; +} + +Clipperz.Base.extend(Clipperz.PM.Proxy.OfflineCache, Clipperz.PM.Proxy, { + + 'toString': function () { + return "Clipperz.PM.Proxy.OfflineCache"; + }, + + //------------------------------------------------------------------------- + +// 'dataStore': function () { +// return this._dataStore; +// }, + + //------------------------------------------------------------------------- + + 'sendMessage': function(aFunctionName, someParameters) { + throw Clipperz.Base.exception.MethodNotImplementedYet; +// return this.dataStore().processMessage(aFunctionName, someParameters); + }, + + //------------------------------------------------------------------------- + + __syntaxFix__: "syntax fix" + +}); + diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js index 2a03fdf..b9d7adf 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js @@ -393,32 +393,36 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.BaseComponent, /*Ext.Compo result = this._ids[aValue]; if (typeof(result) == 'undefined') { _Clipperz_PM_Components_base_id_ ++; result = "Clipperz_PM_Components_" + aValue + "_" + _Clipperz_PM_Components_base_id_; this._ids[aValue] = result; } } else { // result = Clipperz.PM.UI.Common.Components.BaseComponent.superclass.getId.call(this); throw "call to BaseComponent.getId with an undefined value"; } return result; }, + 'getAnchor': function (aValue) { + return '#' + this.getId(aValue); + }, + //------------------------------------------------------------------------- 'getElement': function(aValue) { return Clipperz.DOM.get(this.getId(aValue)); }, //------------------------------------------------------------------------- 'hideElement': function(anElementName) { MochiKit.Style.hideElement(this.getElement(anElementName)); }, 'showElement': function(anElementName) { MochiKit.Style.showElement(this.getElement(anElementName)); }, diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js index 716d851..1010c9d 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js @@ -49,57 +49,65 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Com return "Clipperz.PM.UI.Common.Components.Button component"; }, //------------------------------------------------------------------------- 'text': function () { return this._text; }, 'isDefault': function () { return this._isDefault; }, //------------------------------------------------------------------------- 'renderSelf': function () { - this.append(this.element(), {tag:'div', id:this.getId('wrapper'), cls:'button_wrapper', children:[ +/* + this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button_wrapper', children:[ {tag:'div', id:this.getId('bodyWrapper'), cls:'button_bodyWrapper', children:[ {tag:'div', id:this.getId('body'), cls:'button_body', children:[ {tag:'span', html:this.text()} ]}, {tag:'div', id:this.getId('footer'), cls:'button_footer'} ]} ]}); +*/ +/* + this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button', children:[ + {tag:'span', html:this.text()} + ]}); +*/ + this.append(this.element(), {tag:'a', id:this.getId('button'), cls:'button', html:this.text()}); if (this.isDefault()) { - MochiKit.DOM.addElementClass(this.getId('wrapper'), 'default'); + MochiKit.DOM.addElementClass(this.getId('button'), 'default'); } - MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseenter', this, 'handleOnMouseEnter'); - MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseleave', this, 'handleOnMouseLeave'); - MochiKit.Signal.connect(this.getId('wrapper'), 'onmousedown', this, 'handleOnMouseDown'); - MochiKit.Signal.connect(this.getId('wrapper'), 'onclick', this, 'handleOnClick'); +// MochiKit.Signal.connect(this.getId('button'), 'onmouseenter', this, 'handleOnMouseEnter'); +// MochiKit.Signal.connect(this.getId('button'), 'onmouseleave', this, 'handleOnMouseLeave'); +// MochiKit.Signal.connect(this.getId('button'), 'onmousedown', this, 'handleOnMouseDown'); + MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'handleOnClick'); }, //------------------------------------------------------------------------- - +/* 'handleOnMouseEnter': function (anEvent) { - MochiKit.DOM.addElementClass(this.getId('wrapper'), 'hover'); + MochiKit.DOM.addElementClass(this.getId('button'), 'hover'); }, 'handleOnMouseLeave': function (anEvent) { - MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'hover'); - MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'clicked'); + MochiKit.DOM.removeElementClass(this.getId('button'), 'hover'); + MochiKit.DOM.removeElementClass(this.getId('button'), 'clicked'); }, 'handleOnMouseDown': function (anEvent) { - MochiKit.DOM.addElementClass(this.getId('wrapper'), 'clicked'); + MochiKit.DOM.addElementClass(this.getId('button'), 'clicked'); }, - +*/ 'handleOnClick': function (anEvent) { MochiKit.Signal.signal(this, 'onclick', anEvent); }, - + //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js index 1992154..1d816a9 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js @@ -85,77 +85,95 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.SimpleMessagePanel, Clippe 'setText': function (aValue) { this._text = aValue; if (this.getElement('text') != null) { this.getElement('text').innerHTML = aValue; } }, //------------------------------------------------------------------------- 'type': function () { return this._type; }, 'setType': function (aValue) { - if (this.getElement('icon') != null) { - MochiKit.DOM.removeElementClass(this.getId('icon'), this._type); - MochiKit.DOM.addElementClass(this.getId('icon'), aValue); - } +// if (this.getElement('icon') != null) { +// MochiKit.DOM.removeElementClass(this.getId('icon'), this._type); +// MochiKit.DOM.addElementClass(this.getId('icon'), aValue); +// } this._type = aValue; }, + 'icon': function () { + var type = this.type(); + var result; + + if (type == 'ALERT') { + result = '!'; + } else if (type == 'INFO') { + result = 'i'; + } else if (type == 'ERROR') { + result = '!'; + } + + return result; + }, + //------------------------------------------------------------------------- 'buttons': function () { return this._buttons; }, 'setButtons': function (someValues) { MochiKit.Iter.forEach(this.buttonComponents(), MochiKit.Base.methodcaller('clear')); this._buttons = someValues; if (this.getElement('buttonArea') != null) { this.renderButtons(); } }, //......................................................................... 'buttonComponents': function () { return this._buttonComponents; }, //------------------------------------------------------------------------- 'renderSelf': function() { this.append(this.element(), {tag:'div', cls:'SimpleMessagePanel', id:this.getId('panel'), children: [ - {tag:'div', cls:'header', children:[]}, +// {tag:'div', cls:'header', children:[]}, {tag:'div', cls:'body', children:[ - {tag:'div', id:this.getId('icon'), cls:'img ' + this.type(), children:[{tag:'div'}]}, +// {tag:'div', id:this.getId('icon'), cls:'img ' + this.type(), children:[{tag:'div'}]}, + {tag:'div', /*id:this.getId('icon'),*/ cls:'img ' + this.type(), children:[{tag:'canvas', id:this.getId('icon')}]}, {tag:'h3', id:this.getId('title'), html:this.title()}, {tag:'p', id:this.getId('text'), html:this.text()}, {tag:'div', id:this.getId('container')}, {tag:'div', id:this.getId('buttonArea'), cls:'buttonArea', children:[]} - ]}, - {tag:'div', cls:'footer', children:[]} + ]} +// {tag:'div', cls:'footer', children:[]} ]}); + Clipperz.PM.UI.Canvas.marks[this.icon()](this.getElement('icon'), "#ffffff"); + MochiKit.Signal.connect(this.getId('panel'), 'onkeydown', this, 'keyDownHandler'); this.renderButtons(); }, //------------------------------------------------------------------------- 'renderButtons': function () { this.getElement('buttonArea').innerHTML = ''; MochiKit.Base.map(MochiKit.Base.bind(function (aButton) { var buttonElement; var buttonComponent; // element = this.append(this.getElement('buttonArea'), {tag:'div', cls:'button' + (aButton['isDefault'] === true ? ' default' : ''), children:[ // {tag:'a', href:'#'/*, id:this.getId('buttonLink')*/, html:aButton['text']} diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js new file mode 100644 index 0000000..32dfa63 --- a/dev/null +++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js @@ -0,0 +1,299 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz Community Edition. +Clipperz Community Edition is an online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see + <http://www.gnu.org/licenses/>. + +*/ + +Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components'); + +Clipperz.PM.UI.Mobile.Components.CardDetail = function(args) { + args = args || {}; + + Clipperz.PM.UI.Mobile.Components.CardDetail.superclass.constructor.apply(this, arguments); + +// this._cardReference = null; + + return this; +} + +//============================================================================= + +Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, { + + //------------------------------------------------------------------------- + + 'toString': function () { + return "Clipperz.PM.UI.Mobile.Components.CardDetail component"; + }, + + //------------------------------------------------------------------------- +/* + 'cardReference': function () { + return this._cardReference; + }, + + 'setCardReference': function (aValue) { + this._cardReference = aValue; + }, +*/ + //------------------------------------------------------------------------- + + 'renderSelf': function () { +console.log("CardDetail.renderSelf"); + this.append(this.element(), {tag:'div', cls:'cardDetail', children:[ + {tag:'div', cls:'toolbar', children:[ + {tag:'a', href:'#', cls:'back', html:"List"}, + {tag:'h1', id:this.getId('cardTitle'), html:"…"} + ]}, + {tag:'div', cls:'scroll', id:this.getId('cardDetails'), children:[ + ]} + ]}); + }, +/* + 'renderSelf': function() { + this.append(this.element(), [ + {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[ + {tag:'div', id:this.getId('progressBar')} //, + ]} + ]); + + this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')})); + MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0); + }, +*/ + + 'setTitle': function (aValue) { + this.getElement('cardTitle').innerHTML = aValue; + }, + + 'fieldListElement': function () { + var result; + + result = this.getElement('fieldList'); + if (result == null) { + result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('fieldList')}); + } + + return result; + }, + + 'renderFieldValues': function (someFieldValues) { + var fieldClass; + + if ((someFieldValues['actionType'] != 'NONE') || (someFieldValues['label'] != '') && (someFieldValues['value'] != '')) { + if (someFieldValues['isHidden'] == true) { + fieldClass = 'password'; + } else { + fieldClass = ''; + } + + this.append(this.fieldListElement(), {tag:'li', cls:'cardField', children:[ + {tag:'a', href:'#', cls:fieldClass, html:someFieldValues['value'], children:[ + {tag:'small', cls:'label', html:someFieldValues['label']} + ]} + ]}) + } + }, + + 'addField': function (aField) { + var deferredResult; + var fieldValues; + + fieldValues = {}; + deferredResult = new Clipperz.Async.Deferred("CardDetail.addField", {trace:false}); + deferredResult.addMethod(aField, 'label'); + deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; }); + deferredResult.addMethod(aField, 'value'); + deferredResult.addCallback(function (aValue) { fieldValues['value'] = aValue; }); + deferredResult.addMethod(aField, 'actionType'); + deferredResult.addCallback(function (aValue) { fieldValues['actionType'] = aValue; }); + deferredResult.addMethod(aField, 'isHidden'); + deferredResult.addCallback(function (aValue) { fieldValues['isHidden'] = aValue; }); + deferredResult.addMethod(this, 'renderFieldValues', fieldValues); + deferredResult.callback(); + + return deferredResult; + }, + + //------------------------------------------------------------------------- + + 'directLoginElement': function () { + var result; + + result = this.getElement('directLoginList'); + if (result == null) { + this.append(this.getElement('cardDetails'), {tag:'h2', html:"Direct login"}); + result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('directLoginList')}); + } + + return result; + }, + + 'addDirectLogin': function (aDirectLogin) { + this.append(this.directLoginElement(), {tag:'li', cls:'directLogin forward', children:[ + {tag:'a', href:'#', html:"direct login", children:[ + {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:'http://www.clipperz.com/favicon.ico'}]} + ]} + ]}) + +console.log("ADD DIRECT LOGIN", aDirectLogin); + }, + + //========================================================================= + + 'showCard': function (aCard) { + var deferredResult; + +// this.render(); + +console.log("CardDetail.showCard", aCard); + deferredResult = new Clipperz.Async.Deferred("CardDetail.showCard", {trace:false}); + deferredResult.addMethod(aCard, 'label'); + deferredResult.addMethod(this, 'setTitle'); + + deferredResult.addMethod(aCard, 'fields'); + deferredResult.addCallback(MochiKit.Base.values); + deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')); + + deferredResult.addMethod(aCard, 'directLogins'); + deferredResult.addCallback(MochiKit.Base.values); + deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addDirectLogin')); + + + deferredResult.callback(); + + return deferredResult; +// return Clipperz.Async.callbacks("CardDialogController.updateComponentState", [ +// MochiKit.Base.method(this.record(), 'hasPendingChanges'), +// MochiKit.Base.method(this.cardDialogComponent(), 'setShouldEnableSaving'), +// +// MochiKit.Base.method(this.record(), 'label'), +// MochiKit.Base.method(this.cardDialogComponent(), 'setTitle'), +// MochiKit.Base.method(this.record(), 'notes'), +// MochiKit.Base.method(this.cardDialogComponent(), 'setNotes'), +// +// MochiKit.Base.method(this.record(), 'fields'), +// MochiKit.Base.values, +// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithField')), +// +// MochiKit.Base.method(this.record(), 'directLogins'), +// MochiKit.Base.values, +// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithDirectLogin')), +// +// MochiKit.Base.method(this.cardDialogComponent(), 'resetNewFieldInputs'), +// MochiKit.Base.noop +// ], {trace:false}); + + }, + + //========================================================================= + + 'showCardDetails': function (someData) { + this.element().innerHTML = ''; + this.append(this.element(), [ + {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) { + return {tag:'div', cls:'row', children:[ + {tag:'label', html:aFieldData['label']}, +// {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), html:aFieldData['value']} + {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), children:[ + {tag:'div', children:[{tag:'p', html:aFieldData['value']}]} + ]} +// {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), value:aFieldData['value'], disabled:true} + + ]} + }, someData['fields'])} + ]); + + MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) { + MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); }) + }, this)); + + if (someData['directLogins'].length > 0) { + this.append(this.element(), [ + {tag:'h2', html:"Direct logins"}, + {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) { + return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[ + {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']}, +// {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']} + {tag:'span', cls:'directLogin', html:aDirectLoginData['label']} + ]} + }, someData['directLogins'])} + ]); + + MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) { + MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler'); + }, this), + MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row']) + ) + }; + + if (someData['notes'] != '') { + this.append(this.element(), [ + {tag:'h2', html:"Notes"}, + {tag:'fieldset', id:this.getId('fieldset'), children:[ + {tag:'div', cls:'row notes', children:[ + {tag:'span', html:someData['notes']} + ]} + ]} + ]); + }; + + return true; + }, + + //------------------------------------------------------------------------- +/* + 'toggleClickHandler': function (anEvent) { + var nextState; + var fieldValue; + +//console.log("TOGGLE"); + anEvent.preventDefault; + fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0]; + + nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true'); + if (nextState) { + MochiKit.DOM.removeElementClass(fieldValue, 'clear'); + } else { + MochiKit.DOM.addElementClass(fieldValue, 'clear'); + } + + MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState); + }, +* / + //========================================================================= +/* + 'directLoginClickHandler': function (anEvent) { + anEvent.preventDefault(); + + if (/(directLogin_)/.test(anEvent.src().id)) { + var directLoginReference; + + directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2]; + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference}); + } + }, +*/ + //========================================================================= + + __syntaxFix__: "syntax fix" +}); diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardList.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardList.js index c3f2701..a4aa212 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardList.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardList.js @@ -10,84 +10,137 @@ refer to http://www.clipperz.com. * Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see <http://www.gnu.org/licenses/>. */ -Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components'); +Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components'); -Clipperz.PM.UI.iPhone.Components.CardList = function(args) { +Clipperz.PM.UI.Mobile.Components.CardList = function(args) { args = args || {}; - Clipperz.PM.UI.iPhone.Components.CardList.superclass.constructor.apply(this, arguments); + Clipperz.PM.UI.Mobile.Components.CardList.superclass.constructor.apply(this, arguments); this._cardDetail = null; return this; } //============================================================================= -Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, { +Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, { //------------------------------------------------------------------------- 'toString': function () { - return "Clipperz.PM.UI.iPhone.Components.CardList component"; + return "Clipperz.PM.UI.Mobile.Components.CardList component"; }, //------------------------------------------------------------------------- - 'renderSelf': function(/*aContainer, aPosition*/) { - this.append(this.element(), [ - {tag:'div', cls:'toolbar', id:'toolbar', children:[ - {tag:'h1', id:'pageTitle', html:"cards"}, - {tag:'a', id:'backButton', cls:'button', href:'#', html:"cards"} + 'renderSelf': function () { + this.append(this.element(), {tag:'div', cls:'cardList', children:[ + {tag:'div', cls:'toolbar', children:[ + {tag:'h1', html:"clipperz"}, +// {tag:'input', name:'search', type:'search', autocomplete:'off', placeholder:"search", id:this.getId('search')}, + {tag:'a', href:'#', id:'settings', cls:'button', html:"*"} ]}, - {tag:'div', cls:'cardList', id:this.getId('cardList'), children:[ - {tag:'form', title:'search', cls:'panel cardListSearchForm', id:this.getId('cardListSearchForm'), children:[ - {tag:'input', type:'search', name:'search', value:"", placeholder:"search", id:this.getId('searchField')} - ]}, - {tag:'ul', cls:'panel cardListPanel', id:this.getId('cardListPanel'), children:[]} - ]}, - {tag:'div', cls:'panel cardDetailPanel', id:this.getId('cardDetail')} - ]); - - MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this, 'searchHandler'); - MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this, 'searchHandler'); - MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this, 'searchHandler'); + {tag:'div', cls:'scroll', id:this.getId('listBox'), children:[ + {tag:'ul', cls:'rounded', id:this.getId('list'), children:[ + {tag:'li', html:'loading'} + ]} + ]} + ]}); + + MochiKit.Signal.connect(this.getElement('list'), 'onclick', this, 'cardSelectionHandler'); + MochiKit.Signal.connect(this.getElement('list'), 'ontouchstart', this, 'cardSelectionHandler'); +// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this, 'searchHandler'); +// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this, 'searchHandler'); +// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this, 'searchHandler'); - MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this, 'cardListClickHandler'); - MochiKit.Signal.connect('backButton', 'onclick', this, 'backButtonClickHandler'); +// MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this, 'cardListClickHandler'); +// MochiKit.Signal.connect('backButton', 'onclick', this, 'backButtonClickHandler'); - MochiKit.Style.hideElement('backButton'); - MochiKit.Style.hideElement(this.getElement('cardDetail')); +// MochiKit.Style.hideElement('backButton'); +// MochiKit.Style.hideElement(this.getElement('cardDetail')); }, - //------------------------------------------------------------------------- + 'showCards': function (someCards) { + var cardListElement; + if (this.isFullyRendered() == false) { + this.render(); + }; + + cardListElement = this.getElement('list') + + 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') + }; + +//console.log("someCards", someCards); + deferredResult = new Clipperz.Async.Deferred("CardList.showCards", {trace:false}); + deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false})); + deferredResult.addCallback(Clipperz.Async.collectAll); + deferredResult.addCallback(MochiKit.Base.methodcaller('sort', Clipperz.Base.caseInsensitiveKeyComparator('label'))); + deferredResult.addCallbackPass(MochiKit.DOM.replaceChildNodes, cardListElement); +// deferredResult.addCallbackPass(MochiKit.DOM.removeElementClass, cardListElement, 'loading'); + deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'appendCardToList', cardListElement)); + deferredResult.callback(someCards); + }, + + 'appendCardToList': function (aCardListElement, aCardInfo) { +//console.log("appendCardToList", aCardInfo); + this.append(aCardListElement, {tag:'li', cls:'cardListItem arrow', cardreference:aCardInfo['_reference'], children:[ + {tag:'a', href:'#', html:aCardInfo['label'], children:[ + {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:aCardInfo['favicon']}]} + ]} + ]}); + }, + + 'cardSelectionHandler': function (anEvent) { + var listElement; + var cardReference; + + anEvent.preventDefault(); + + listElement = anEvent.target(); + if (MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') == null) { + listElement = MochiKit.DOM.getFirstParentByTagAndClassName(anEvent.target(), tagName='li', className='cardListItem'); + } + cardReference = MochiKit.DOM.getNodeAttribute(listElement, 'cardreference'); +console.log("###", listElement, cardReference); + // TODO: Notify card with reference MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') has been selected + MochiKit.Signal.signal(this, 'selectedCard', cardReference); + }, + //------------------------------------------------------------------------- +/* 'searchHandler': function (anEvent) { if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ENTER')) { // RETURN anEvent.preventDefault(); } else { if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ESCAPE')) { anEvent.target().value = ""; } if (anEvent.type() == 'keyup') { MochiKit.Signal.signal(this, 'searchEvent', anEvent.target().value); } } }, //------------------------------------------------------------------------- @@ -114,33 +167,33 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C 'cardListClickHandler': function (anEvent) { anEvent.preventDefault(); if (/(cardListReference_|cardListItem_)/.test(anEvent.target().id)) { var cardListReference; cardListReference = anEvent.target().id.match(/(cardListReference_|cardListItem_)(.*)/)[2]; //console.log("Showing detail for card named", cardListReference); MochiKit.Signal.signal(this, 'selectedCard', cardListReference); } }, //========================================================================= 'cardDetail': function (someData) { if (this._cardDetail == null) { - this._cardDetail = new Clipperz.PM.UI.iPhone.Components.CardDetail({element:this.getElement('cardDetail')}); + this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:this.getElement('cardDetail')}); } return this._cardDetail; }, //------------------------------------------------------------------------- 'removeCardDetail': function () { if (this._cardDetail != null) { this._cardDetail.remove(); this._cardDetail = null; } }, //========================================================================= @@ -182,20 +235,20 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C anEvent.preventDefault(); MochiKit.DOM.getElement('pageTitle').innerHTML = "cards"; offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480); MochiKit.Style.setElementPosition(this.getElement('cardList'), {x:-offset}); MochiKit.DOM.showElement(this.getElement('cardList')); new MochiKit.Visual.Parallel([ new MochiKit.Visual.Move(this.getElement('cardList'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}), new MochiKit.Visual.Move(this.getElement('cardDetail'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}), MochiKit.Visual.fade (this.getElement('cardDetail'), { transition:MochiKit.Visual.Transitions.linear, sync:true}), MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true}) ], {duration:1, afterFinish:MochiKit.Base.method(this, 'removeCardDetail')}) }, - +*/ //========================================================================= __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js new file mode 100644 index 0000000..eafcdbc --- a/dev/null +++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js @@ -0,0 +1,356 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz Community Edition. +Clipperz Community Edition is an online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see + <http://www.gnu.org/licenses/>. + +*/ + +Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components'); + +Clipperz.PM.UI.Mobile.Components.LoginForm = function(args) { + args = args || {}; + + this._pin = ''; + + this._message = null; + this._steps = 0; + this._actualSteps = 0; + + this._callback = null; + this._errorCallback = null; + + this._mode = 'CREDENTIALS'; + + Clipperz.PM.UI.Mobile.Components.LoginForm.superclass.constructor.apply(this, arguments); + + return this; +} + +//============================================================================= + +Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, { + + //------------------------------------------------------------------------- + + 'toString': function () { + return "Clipperz.PM.UI.Mobile.Components.LoginForm component"; + }, + + //------------------------------------------------------------------------- + + 'callback': function () { + return this._callback; + }, + + 'errorCallback': function () { + return this._errorCallback; + }, + + //------------------------------------------------------------------------- + + 'mode': function () { + return this._mode; + }, + + 'setMode': function (aValue) { + this._mode = aValue; + }, + + //.......................................................................... + + 'pin': function () { + return this._pin; + }, + + 'setPin': function (aValue) { + this._pin = aValue; + }, + + //.......................................................................... + + 'username': function () { + return this._username; + }, + + 'setUsername': function (aValue) { + this._username = aValue; + }, + + //.......................................................................... + + 'passphrase': function () { + return this._passphrase; + }, + + 'setPassphrase': function (aValue) { + this._passphrase = aValue; + }, + + //------------------------------------------------------------------------- + + 'message': function () { + return this._message; + }, + + '_setMessage': function (aValue) { + this._message = aValue; + + if (aValue == null) { + MochiKit.Style.hideElement(this.getElement('credentialsMessage')); + } else { + this.getElement('message').innerHTML = aValue; + MochiKit.Style.showElement(this.getElement('credentialsMessage')); + } + }, + + 'setMessage': function (aValue) { + this._setMessage(aValue); + MochiKit.DOM.removeElementClass(this.getElement('credentialsMessage'), 'error'); + }, + + 'setErrorMessage': function (aValue) { + this._setMessage(aValue); + MochiKit.DOM.addElementClass(this.getElement('credentialsMessage'), 'error'); + }, + + //------------------------------------------------------------------------- + + 'setCallbacks': function (args) { + this._callback = args['callback']; + this._errorCallback = args['errorCallback']; + }, + + 'showErrors': function (args) { +//console.log("LoginForm.showErrors", args); + if (args['previousFailedAttempt'] == 'LOGIN') { + this.setErrorMessage("Wrong credentials"); + } else if (args['previousFailedAttempt'] == 'PIN') { + if (args['failedAttempts'] == -1) { + this.setErrorMessage("Wrong PIN - Resetted"); + } else { + this.setErrorMessage("Wrong PIN"); + } + } else { + this.setMessage(null); + } + }, + + 'updateWithArgs': function (args) { + this.renderIfNeeded(); + this.setCallbacks(args); + this.showErrors(args); + this.updateRendering(); + }, + + 'showPinLogin': function (args) { + this.setPin(''); + this.setMode('PIN'); + this.updateWithArgs(args); + +// $(this.getAnchor('PIN')).focus(); + this.getElement('PIN').focus(); + }, + + 'showCredentialsLogin': function (args) { + this.setMode('CREDENTIALS'); + this.updateWithArgs(args); + + if (this.getElement('usernameField').value.length == 0) { +// $(this.getAnchor('usernameField')).focus(); + this.getElement('usernameField').focus(); + } else { +// $(this.getAnchor('passphraseField')).focus(); + this.getElement('passphraseField').focus(); + this.getElement('passphraseField').select(); + } + }, + + //------------------------------------------------------------------------- + + 'renderIfNeeded': function () { + if (this.isFullyRendered() == false) { + this.render(); + }; + this.updateRendering(); + }, + + 'updateRendering': function () { + MochiKit.Style.showElement(this.getElement('credentialsBody')); + MochiKit.Style.hideElement(this.getElement('validating')); + +// this.hideAllPanes(); + MochiKit.Base.map(function (aNode) { MochiKit.Style.hideElement(aNode); }, MochiKit.Selector.findDocElements('div.credentialsBody > div')); + if (this.mode() == 'CREDENTIALS') { + selectedPanel = this.getElement('credentials') + } else if (this.mode() == 'PIN') { + selectedPanel = this.getElement('pin') +// this.updatePinDisplay(); + } else { + throw 'Unhandled login form mode'; + } + MochiKit.Style.showElement(selectedPanel); + + MochiKit.Style.hideElement(this.getElement('validating')); + }, + + 'renderSelf': function() { + var selectedPanel; + this.append(this.element(), {tag:'div', id:'login', children:[ + {tag:'div', cls:'toolbar', children:[ + {tag:'h1', html:"clipperz"} + ]}, + {tag:'div', cls:'scroll', children:[ + //================================================================== + {tag:'div', cls:'credentialsMessage', id:this.getId('credentialsMessage'), children:[ + {tag:'h1', cls:'message', id:this.getId('message'), html:"Message"} + ]}, + //================================================================== + {tag:'div', cls:'credentialsBody', id:this.getId('credentialsBody'), children:[ + //-------------------------------------------------------------- + {tag:'div', cls:'pin', id:this.getId('pin'), children:[ + {tag:'form', cls:'scroll', id:this.getId('pinForm'), children:[ + {tag:'ul', cls:'edit rounded', children:[ + {tag:'li', children:[{tag:'input', type:'number', name:'PIN', placeholder:"PIN", id:this.getId('PIN') }]}, + ]}, + {tag:'a', href:'#', cls:'greenButton', id:this.getId('pinSubmitButton'), html:"Login"} + ]} + ]}, + //-------------------------------------------------------------- + {tag:'div', cls:'credentials', id:this.getId('credentials'), children:[ + {tag:'form', cls:'scroll', id:this.getId('credentialsForm'), children:[ + {tag:'ul', cls:'edit rounded', children:[ + {tag:'li', children:[{tag:'input', type:'email', name:'name', /*value:'joe',*/ placeholder:"username", id:this.getId('usernameField') }]}, + {tag:'li', children:[{tag:'input', type:'password', name:'passphrase', /*value:'clipperz',*/ placeholder:"passphrase", id:this.getId('passphraseField') }]} + ]}, + {tag:'a', href:'#', cls:'greenButton', id:this.getId('credentialsSubmitButton'), html:"Login"} +// {tag:'input', type:'submit', cls:'greenButton', id:this.getId('credentialsSubmitButton'), value:"Login"} + + ]} + ]}, + //-------------------------------------------------------------- + ]}, + //================================================================== + {tag:'div', cls:'validating', id:this.getId('validating'), children:[ + {tag:'div', cls:'loading', children:[ + {tag:'div', cls:'spinner', children:[ + {tag:'div', cls:'bar01'}, + {tag:'div', cls:'bar02'}, + {tag:'div', cls:'bar03'}, + {tag:'div', cls:'bar04'}, + {tag:'div', cls:'bar05'}, + {tag:'div', cls:'bar06'}, + {tag:'div', cls:'bar07'}, + {tag:'div', cls:'bar08'}, + {tag:'div', cls:'bar09'}, + {tag:'div', cls:'bar10'}, + {tag:'div', cls:'bar11'}, + {tag:'div', cls:'bar12'} + ]} + ]}, + {tag:'div', id:this.getId('loadingMessage')}, + {tag:'a', href:'#', cls:'grayButton', id:this.getId('loginCancelButton'), html:"Cancel"} + ]} + //================================================================== + ]} + ]}); + + MochiKit.Signal.connect(this.getElement('credentialsForm'), 'onsubmit', this, 'submitCredentialsHandler'); + MochiKit.Signal.connect(this.getElement('credentialsSubmitButton'), 'onclick', this, 'submitCredentialsHandler'); + + MochiKit.Signal.connect(this.getElement('pinForm'), 'onsubmit', this, 'submitPinHandler'); + MochiKit.Signal.connect(this.getElement('pinSubmitButton'), 'onclick', this, 'submitPinHandler'); + + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'initProgress', this, 'initProgressHandle'); + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'updateProgress', this, 'updateProgressHandle'); + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'advanceProgress', this, 'advanceProgressHandle'); + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'progressDone', this, 'progressDoneHandle'); + }, + + //------------------------------------------------------------------------- + + 'submitPinHandler': function (anEvent) { + var pin; + + this.setMessage(null); + pin = this.getElement('PIN').value; +// $(this.getAnchor('PIN')).blur(); + this.getElement('PIN').blur(); + + credentials = Clipperz.PM.PIN.credentialsWithPIN(pin); + this.loginWithCredentials(credentials); + }, + + 'submitCredentialsHandler': function (anEvent) { +//console.log("submitCredentialsHandler"); + var credentials; + + this.setMessage(null); + + credentials = {}; + credentials['username'] = this.getElement('usernameField').value; + credentials['passphrase'] = this.getElement('passphraseField').value; +// $(this.getAnchor('passphraseField')).blur(); + this.getElement('passphraseField').blur(); + + this.loginWithCredentials(credentials); + }, + + //------------------------------------------------------------------------- + + 'loginWithCredentials': function (someCredentials) { + var args; + + args = {}; + args['credentials'] = someCredentials; + args['errorCallback'] = this.errorCallback(); + + MochiKit.Style.hideElement(this.getElement('credentialsBody')); + MochiKit.Style.showElement(this.getElement('validating')); + + MochiKit.Async.callLater(0.1, this.callback(), args); + }, + + //------------------------------------------------------------------------- + + 'initProgressHandle': function (anEvent) { +//console.log("** initProgressHandle", anEvent); + this._steps = anEvent['steps']; + this._actualSteps = 0; + }, + + 'updateProgressHandle': function (anEvent) { +//console.log("** updateProgressHandle", anEvent); + this._steps += anEvent['extraSteps']; + }, + + 'advanceProgressHandle': function (anEvent) { +//console.log("** advanceProgressHandle", anEvent); + this._actualSteps ++; +//console.log("STEPS: " + this._actualSteps + "/" + this._steps); + }, + + 'progressDoneHandle': function (anEvent) { +//console.log("** progressDoneHandle", anEvent); + }, + + //------------------------------------------------------------------------- + __syntaxFix__: "syntax fix" +}); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js new file mode 100644 index 0000000..12a61f7 --- a/dev/null +++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js @@ -0,0 +1,393 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz Community Edition. +Clipperz Community Edition is an online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see + <http://www.gnu.org/licenses/>. + +*/ + +Clipperz.Base.module('Clipperz.PM.UI.Mobile.Controllers'); + +Clipperz.PM.UI.Mobile.Controllers.MainController = function() { + this._jQTouch = null; + this._user = null; + this._proxy = null; + this._loginForm = null; + this._cardList = null; + this._cardDetail = null; + + return this; +} + +MochiKit.Base.update(Clipperz.PM.UI.Mobile.Controllers.MainController.prototype, { + + 'toString': function () { + return "Clipperz.PM.UI.Mobile.Controllers.MainController"; + }, + + //------------------------------------------------------------------------- + + 'user': function () { + return this._user; + }, + + 'setUser': function (aValue) { + this._user = aValue; + }, + + //------------------------------------------------------------------------- + + 'jQTouch': function () { + return this._jQTouch; + }, + + 'setJQTouch': function (aValue) { + this._jQTouch = aValue; + }, + + //========================================================================= + + 'run': function () { + console.log("MainController.run"); + + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'doLogin', MochiKit.Base.method(this, 'doLogin')); + Clipperz.DOM.Helper.overwrite(MochiKit.DOM.currentDocument().body, {tag:'div', id:'jqt', children:[ + {tag:'div', id:'loginForm'}, + {tag:'div', id:'cardList'}, + {tag:'div', id:'cardDetail'}, + {tag:'div', id:'preferences'} + ]}); + + this.showLoginForm(); + + this.initjQTouch(); + + +// this.showAddToHomeScreenBaloon(); +// this.selectInitialProxy(); + }, + + 'initjQTouch': function () { + var jqt; + + jqt = new $.jQTouch({ + icon: 'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAHIAAAByCAIAAAAAvxIqAAAD8GlDQ1BJQ0MgUHJvZmlsZQAAKJGNVd1v21QUP4lvXKQWP6Cxjg4Vi69VU1u5GxqtxgZJk6XpQhq5zdgqpMl1bhpT1za2021Vn/YCbwz4A4CyBx6QeEIaDMT2su0BtElTQRXVJKQ9dNpAaJP2gqpwrq9Tu13GuJGvfznndz7v0TVAx1ea45hJGWDe8l01n5GPn5iWO1YhCc9BJ/RAp6Z7TrpcLgIuxoVH1sNfIcHeNwfa6/9zdVappwMknkJsVz19HvFpgJSpO64PIN5G+fAp30Hc8TziHS4miFhheJbjLMMzHB8POFPqKGKWi6TXtSriJcT9MzH5bAzzHIK1I08t6hq6zHpRdu2aYdJYuk9Q/881bzZa8Xrx6fLmJo/iu4/VXnfH1BB/rmu5ScQvI77m+BkmfxXxvcZcJY14L0DymZp7pML5yTcW61PvIN6JuGr4halQvmjNlCa4bXJ5zj6qhpxrujeKPYMXEd+q00KR5yNAlWZzrF+Ie+uNsdC/MO4tTOZafhbroyXuR3Df08bLiHsQf+ja6gTPWVimZl7l/oUrjl8OcxDWLbNU5D6JRL2gxkDu16fGuC054OMhclsyXTOOFEL+kmMGs4i5kfNuQ62EnBuam8tzP+Q+tSqhz9SuqpZlvR1EfBiOJTSgYMMM7jpYsAEyqJCHDL4dcFFTAwNMlFDUUpQYiadhDmXteeWAw3HEmA2s15k1RmnP4RHuhBybdBOF7MfnICmSQ2SYjIBM3iRvkcMki9IRcnDTthyLz2Ld2fTzPjTQK+Mdg8y5nkZfFO+se9LQr3/09xZr+5GcaSufeAfAww60mAPx+q8u/bAr8rFCLrx7s+vqEkw8qb+p26n11Aruq6m1iJH6PbWGv1VIY25mkNE8PkaQhxfLIF7DZXx80HD/A3l2jLclYs061xNpWCfoB6WHJTjbH0mV35Q/lRXlC+W8cndbl9t2SfhU+Fb4UfhO+F74GWThknBZ+Em4InwjXIyd1ePnY/Psg3pb1TJNu15TMKWMtFt6ScpKL0ivSMXIn9QtDUlj0h7U7N48t3i8eC0GnMC91dX2sTivgloDTgUVeEGHLTizbf5Da9JLhkhh29QOs1luMcScmBXTIIt7xRFxSBxnuJWfuAd1I7jntkyd/pgKaIwVr3MgmDo2q8x6IdB5QH162mcX7ajtnHGN2bov71OU1+U0fqqoXLD0wX5ZM005UHmySz3qLtDqILDvIL+iH6jB9y2x83ok898GOPQX3lk3Itl0A+BrD6D7tUjWh3fis58BXDigN9yF8M5PJH4B8Gr79/F/XRm8m241mw/wvur4BGDj42bzn+Vmc+NL9L8GcMn8F1kAcXjEKMJAAAAACXBIWXMAAAsTAAALEwEAmpwYAAABbmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgrlPw1BAAAd7klEQVR4nO19eZQV13nn797a3tr7yg5ikxCIHRohkACBEFqsJY4z8T52nPHYPp74JJ54bMfOsRMf2Z44OZ7EJ16iDLIsS5ZlydJY+2Ii1haiAQFCNGvTNHS/9/pt9Wq93/xRb+9u6OU1wif9O3Wq6223bv3qu7/vu9+9txqYwAQmMIEJTGACE5jABCbwnxTs/a4AHnjggY0bNwohxl4U5/xnP/tZe3v72Iv6g8fDDz9MlcOHP/zh9/uCAEB+vysA27YBkHAh7KK3aSRlMIDAFcZl13UrW73R4f2nNQvXYGYMjANUROkVyWXZvbBIDcLXOH4VHBGuGVoBgAMMYGAAERhAeekfyC8r+svA3n8nUYxrilYXxAFkScz6sKENlgHkfc5AYmSyMc64dmglEOW4pOyOUSlZxS9Y4RVjIHeEcjy+uGZoJYBErmlTdk/5V0NQxnLkUgXiswrimqEVAIkcTQU685+BioJsyikAkDXbCVqHABWslcpopcJfopIuTIFZMSECQ0GAWBGJA2j13mQspw8MeRumayJczeNaorVMW7MxVqkUgIEox6Yo7CdEYAhQji+UuKxyWgcFA0SWWbompODaoRUD7K40Hri8dJIgIVjRt99fXEu0FhNKpWabbfulYJTthjGAXLqW5PVapRUYwGyuj1AcGGTBQAJiQgQGotDFQkFkBwkGcpZbiAK8nqsgEsC10te6FmglAESCeZFAltPiMGvQSAtALhYAsrSSmLDWLIg8Wt0iQgdoa7nXKupm5TuvwgW5NEFrFp6kilyEVGAWGERSvT0rsWgwkEtZeZ2g1YNnX1llRIHNYlq9Dy6Tc4EgciHcPzwRuP/++9Pp9KuvvuoNk1QKRAQIKjBSrABFx9l2P2jvgIFcCAFyxyNunTVj2h/dt82nad/8zg+G+ZPh0ur3+7/70HdnXTdrz54927dvf+qpp7q7u0dbzxJkaaVSESjYbFHbz3NKJSksIOuySFTSZQX8/lvWrPjQg3dvWr92UktTV/eFH/30kYu9fcP57XBpXbOmbebMGURi1apVq1at+upX/9czTz+9/ZGf79q1a8yjciKrjCgKPEvcF8o7tRggBiRIuCQqE2DNnjXjvrs2P3jvHTfMn6sosmmayWSiqaF2w/q2X/zqt8MpYbi0Pvjgg4xzYaXgpIhrLY31f/aZP//EJz6+e/fu7du3P/3Ms5cuXRrlRQjynHhRp55KDgbpdJUIQDbf6hnsGKw1FAysu3nlnzxw94Z1bfX1dY5tO44BV6gwDVcmId+7dWMlaa2urr5j8+0kXLgW4yoTJukpwSQu+2+5Ze0t69Z9/aunnvnts9t//os9e/aO/MIIwi2JBMqZ9YS1zGXlDgRlIwHhQjijE4F5c2Y9eM8dH7hr8/Vzr+MSt03TTPerkutTuCSrINW2kpaptK1YNG3KpLNdV1a/YdG6fv26GTNnuJbByQWTwRQmSZKwyYoLMya4Nqml4bOf+++f/PhHd+7c9cijjz373O96+4alQQCIBIQrhFuqrQNsVgwwWO/TbEfLJRJCjEyOqsKhDevaPnT/tnVrVtbVVlu27ZhpmVl+iWRNYpIMcIDAFZU5KcdsrK3efNuan2z/1RVLHhatD95/Hxgn12CMgbw0vgBjjKuMXC4M0pOOLklycMNt6zZsuPXUyZO/efq3jz72RPv+t69culegtxXKH0DrQB3wDgi5/quLgt+7AhbMn/PgvVvu2bpp7uyZnDPbNKx0ROW2ooBzCVwCGIQAIxCBQ1EVltZdV7l787qfPvLkFVvklWltbGzccNt6YWUYOeBK9pqLXQqXGeOycMiOuWZEcN/USY3/44uf+/QnP/YfO3c98ugvf/fiy9FobKjyCUTCpWy/s6jkkoNSqS3skR0vIEFCkHAvf7011VWb1q/54/vvXLt6WU11tW2ZrpngyAS5KyscXAI4iCBcMMpOWgBBEJd9CqKm6Vu2aP6cWdOOd54ZK60bb1s/eXKrldEVr7kVsZGj2LMRxrgqMUcSGaEnbV1SpNDmjetu33jric7Op37z7GNP/Lrj0DuD8SqyXU+IrHqW8VvSNSjrdCH7KYmsvA4RCSxaMO/Be7bcs3XDdTOnc8ZsM+2kuzVmKpwYl8A4gKx5gmXbfvYAAIFLqkyGaVZXBbfc2lYBWh+47wMggrCYoiBvU0DBlLIXKbLHTOIS4+SQExHxXof5Zk5p/PKXPv+ZT33s9zvefPTxJ198+fX+eKLAKuWttcxlDTDYEmXIcQoAjMj1rLXMZdXX1tx+25oPfmDrmpVLqsJh28oIIyKTHuK2JDEwzzxF1iVmzbOYU++MHIIpisYyumPL226/+V8efsK5bFh5BVqnTpmybm2bbegcAkzKznIgKr0wkXtHZAc/iAAwLktMSCIjMklHl/xSeNuWW7du3nj8vfd+/fRzv3zymSPHjgPw4k0ityhuHZTZPK2D9LgYuQRBJNyc11qy6Po/uueOu7asnzl9GgM5ZkKkzviZoXABzzwJIBdMADyXEfemKnnH3mwwBpY9KVc0hSVM01w0f9YN82YdPPLe6GndvOm2pqYGQ0+qipQjseiCy7qSVEqHRzTjXFI4OYrb5yZ6BfPNmd70lb/8/H/79Eff2LHz4UceD/h9wnUgRDmVA5kt7hqIXMo1a7ACwhWO01BX88cf2PpfHty2avnicDDgWDrpFxSk/MzmHGBSzr95I7gsSxwxMAbGQBxEYLl9Yd4CgUk+hWUyZtAf2Lph9eVpvdyMMMbYs089dsfmjbaR0nwhICthOZPMbdmXOUdMBLjZg8KnORUWthC2Q7LLw5K/1rJhGBlVVbmTCIme3J27jPvK39ocoV7YKpwENTtyfSaTqQqHOBPCjMsirrGMxAisaNJc9oCVHGT3vGjPAF7yDpfITvdG01wJH363c+uffskwraGou5y1zr5u1uoVSy0jI3MGzpF3CESFrcRsaWhNyHk2xjlXVHIh+pxkrwyfxqtclzMigmBe4UPSivLj3I6RICJBIqQJnjmnIqkwh/Gcz/ES5IUpiGzAxnPdNZ4z5LywMoDAOASYrGk8plvW/FlTFi+YvXv/kdHQeueWjbU11YaelNRAzsGW+qtyTUCRBytuy6LoHuQsl0myRDLpqptwhOxCAs9rqyj9eZk4oLQO3idCdmM+iqtM53nz9NpNfg4s5ayVsQK/xLPBMssJQj6uyjLLCxVgkk9T0knDFwjcuWHVaGiVJeneu7Y4tsVATFJzjRrZRsGkrBV4zV84gAPk55rJhebPvD6lneUUZVwzzmWVuSCzkJwelNlifoGi9gGAGEOIp4rUMN8pYIWDPJtULAI5YSWWlVTPeIlyFFPhJUHVfFKy3zLljWsWPxTwp/TMyGi94fp5S2+60TIzmqqAK1lCyYGdgHEJ+jlkeqB3I9MFMwonBWHBixaYDK5BqYIShlYPtR5qPbQGyAFwDRAQTqny5oKY7LG4HLNsqN6Bd0fzg4zFhKJgsN5Bll9WOChQyQr+iigXJOReCmKy6pPchG3Omtq84qZ5r+06MDJa7966KRTwZwxD0kIw+hB/B5G30H8QyfeQPgs7CVE6U7d4CmUxOMBlKGH4WhCYjPBchGbBPwVKFZgMYQFOSc+isC8S2WJmB0lxFZ14YAWGVFWAeM6JFcWqlJvmhXw8kL/xAJP9fiWRMWUlsO225SOjVVXVbXdstB3B4wfZwR8jfgiZbm9MpETlhwlyYMZgxNB/FHgZnEFrRGg2ahai5kb4WsEVCCvX0Sp2WZcV2QKzrNxy84QWuGalNpu3guIDzzsxEM/63oIs5E4toGh+hfWZlrxu5Q211aFYPDVcWpfedOON82dbtu07sx3nXoAMMEAaNo8DwQrXCBCMS8hcQu9OyBpCs1G/HLVL4WsGOMgqUl5RwmNJQrbIa5U4MZQ2HFa6L9PWHJvFzT8byeZdmWe2yPcLmKT6VYrp1pTm2jVL5z/32iDLwAan6rOf+si6taudZLev8/uM9MtGt6NC3mLIRaYXsQ707kD6JJgMrR5czWW1Sw22oLmicFDwhKUDt1lac28ylN+M4o0V3R42YI+8TXg/55ysVNqQZcVx3f/3+lsDr28Qaw0GA1s23mLarhTbzY3eETT20cEr30mjdw/69iA0E03rUbccchCuVS61JRRjQGCAIXWgzGAHD12pYLnFCpv3ltnvQPUFVClhWmbb4tnNDTUX+/rLrmkQa13btvxzn/pTx3H8p34o6acrb6qDIn91Vj+iB9D/NiDga4akFSVSS0W2kJbNezYxiCWiKH4o2CYKRoqylyh6M49SlyjJZCXTGVETDhzpPH/kRFfZ1Qxiivdu3SjLEqwos/rAclW9avDsJnMBpx7Fse8hsjuX3i7rK3sKkGfcBTyP5w6yCe99J7sXInec/0L+uLgXXlaOyKVmBFxTljiERcLdesvCgYvCyq21vq7277/2xYBPcUmyGjba1ctJDjA3ye1EiQ8Yb3iWaycQexv6afiaoNYOYrPFCuvZLyu24sGkEwXnM0iSM29BeYvOvvT8mG0Z6VS8Pxrpj6dcIsZITGqpf+a1t+NJvbj65dpaU1PTr4sWpgb8ZJrMrl5pVa9mdkxJHVajv1fi+ySjuxCNjDe8U/QfReokmtajcT0kDcIq19ZiOkRx+82FBPluqxdpZV96LTUfsRYrbD4lyMEIwrIsR8/YaUOYFhPEOGc+nyakQHfMfvPAKcMub86DcOMPBJctWXzX1k0bb1k5e3qzyoVlmrZgIMacmJI8rMb+Q020S2bP1eMXgABC0zH5bgSmQtil6RgM5rtQFJaiaPVmPmWFLI8exWVJLAYIsmxHN9y0CdOWXMEkiWmaRrK/O+buPnT2+df37W7viEUHGQy9HCWBYGjZ0sXbNm/YtG7ldVMbFeaYpukIBjBmx+TUEV//m2qyXTIvXSV+CZA0tNyO+lUA5TJqZfkt76v5lFXRz7MdqhyhJblBno1bGSCEabm6ibTJTVcWgnHONE2D7L8QFzs7zjz/RvvetzqikcsNLQ+LiVAovHz50js337rx5uUzJ9fJcEzTyPOrpo9o8Z1a8m3J6h13fj2aapegdQskX26tvCgIAkoNtpjZgoWiQKuXnQFBkGmLtMl1SzZdxSUmsSybPQnadfDM82+8tbu9IxrpHU41R0ZAuKp6xfKl225ff9vNS6a31Mhk5+2X21FFP+KL79bSHZLVN778CiA4FVPugVoHYZcSStkeUZmdUqm1slyClYRpU9qS05ZquqogLnFoqgol0JOgXYfOvfBG++72jkjfsNgsPuFoUFVds3LF0m2333rr6oXTmqs4WZZh2h6/TlRNH/Un92rpg5IdGdt5hgYBai0m3w3/lFzWsTgILRNZrw4sqwwgEmTZSFtK2tZMVxPEOSefpkEO9CRo9+FzL/x+/659HZG+Uc6AGuvlVtfUrlq5bNumdetWLpjaFOaukbNfcCem6cd8qX0+/bBkRytxtlIQIPsx6S6EZmXDg9IkbHZNYtZIyWPTdFjaUnXbbwgtZ5sKU4IXE9j9zrnn39i/u72jr3e088lyqNiF1tTWrV65bNumW25ZccOUBj9cwzQMhyQQSU5Myxzzp9p9maOSE6vkaT0n1roFoTmlNlv8DUFCmLaUtrW07TeFTxCXGGmaytTgxQT2HDn//Bv7d+07MHY286i8+NXW1betXHbnprVrl82bXOeHq5uG6RAHSLKjmvFuIH3AZxyR3HhlzkcAV9C6BaHZEFbuLQJAwjVsnra1tBOwXJ8LLjFomsKU4KUk9h7pfmHH2zv3Hui9dLEyNSnCOMZEdfUNa1Yvv3PjzTcvnt1aq1qZpG074DKE3dLzkM94r6I2q6J1CwLTiz2YcO2uRH1a1HLmSpxrgareFPYevfDCjgM79x64dLGnQqcfBFcjlG9oaFq9atmXPrF1drNmu7y2/8ma+PPjo7NboTVCONm3GExTnE22giu9Kf7dR/fs2nfgYs+Fip54cIx31g8A+vouvXfkQLWccYgH0vuqEy+NSwLXyaDnFVj9YJRPoGiq0+S7IFwnLOvxC+9eHU4xtoz/cFFbFfzJ331mxtRWlulqivxUEplxaSQMcE1YUQSn5YYLCUQ+2XZs02Hhm2+asWP/yUhcv3JRY8a408oY+9YXP7ipbaFppJqjD2t29zgKDwPsFIQF/6RszhAEICDrKZ35/eEFsxqf33ncssd90fG40/qRe9d+/sO3G5Zbl3gmnNk/7mLOACsCOQC1Nve4IcE480vpaEqZ3FRXHVJff+vUOFdinGldcv2M7//Vh7ikBvX2huSzjF2tfLjRC18zJBVwvTFwWRYK9IjuWzS7uTemHz5ZsRB1UIwjrXXVwX/+2kcmN9dzs6s18XNO5vidqxzChZNCYFJhDJHIp9iObacs36oFre3HLlzoG2QgulIYL1o5Y9/6wn23rZxvGqnW5KOqc+kqpWU9MMBOg6vQaiGc7AgCQ1DNJHXGJf+S2Y0vt59OG5VcDlmM8aL1o/e0ffZD6zOW25j+Xdg8dFU59cAAKw5/E7iUHxtnHAFFj6TkxprQpMbgK+1nhRgXXRoXWpfdMP2hv7iPMTlk7G/KvPg+cOpBuCAbvgbAzU/LlWWhwuhLaXOn1tiOaD82LiJbeVrra0I//OsPtjTUSGbXJP1JjiHn1g4fBObCx71JicOHJwVqFSStMEWDyKfZruP069ryufXHz/Wf7kmOvYZlqDCtnLNvf+6utUuvs8zU5MyTmohc+TfDQIJd183XVtNJjhGGnEQQJnz12bHu7KA3hTQjlWGuUJfNrdtxsKc/VYF7X4wK0/qxu1f+2QOrM6bbYr4Udt6tSJk2AmfZBh2NAAujfKLDFcAA14AczAVb3ui3YJyCqh5JKOGgOnty+OX9F2ynkg8oqyStyxdM/fvP30lMqrY7mqw3KlXsebQl0crI1NHoR9THRphRJIAs+Gqy8oqcyCpC40ZvXJveHPCr0puHRzascnlUjNaGmuA//dU9TXXVitU9xXp2xDo4BGI0s4dukmVFVv2upaepqYadldhIAiMGuBYUPyRvAVQu3iLh1yzhuNGUsmhWuCdqvNtVMZGtDK0SZ9/+7Ja2hdMcMznV+q1K5VO9RgcLwTNiraJoz3ck955yls/UdJMsBGt5F8NIAiMCIKAGQE7OYL2NQj4jpTPDkpbPDe873t8br4zIVobWT9y97JP3LDFMp9V5vYoq1eNmZ90VGTREDPVLP3jx1Z2Hbm5b0RpIJu2QDCvEoyMpCRA2VB84gRzABhwv6mJMhLRMJKFqsnTjjMArB6KGVQGRrQCtqxZM+daf3yZIqnEONou9Yy/QQ8Sd2ePMUzXtO788tq/juOvYh08n7ly3ULEjSVEX5n0qN0ZQHBE4QeagHKdwAAfkyIrjk81Lca2lRmmslt84FB84aDtSjJXWxtrgD/5iS311UHW6p4lXRhwADQGTQqesFYqqvPKO+Y+PvE4kAPT1RVJUs2Fxo5nRdVFdJ1/gbNiWxQC4UBhgAhZgASZgAzbI8fsM4VJfUp0/WdNN9+DpseZkx0SrJPFvf+bWlTe0OlZqOr2soTKST2Cn7SWGqIrZvr/84Y54vOD63zl+dsacBTe0WClDEiTVKMN9GAQACIJsg9s5g/U2G8wCOeFAOq3L6Yy89DrtaFemKzKmdMGYBl0+vm3hllXTM4bZSrsDGMkVXha99rSY1aCoyv95uvPc+ZJhEhLOd37y4ul0k8rNi2ZzxGoaQc+YADu3uqx4E4AQnNmzWroVnpK58+X7aifVjekJYaO31tU3TvrGJ9oEsTpxpIV1jKUSxciI0CljoSrLb7wn/uHnO2jAv9DJ6OnOXtq6ZiYykYRTXadEZD68YM67AXLpxKwiyIrwK+bFqFwToBmN/LXDpjta7zVKWptrg//7C+trw37N7ZkuvclRmS4KgZ/M3GC4wYQT/PKP9sX6B4/8z3f3SOFpa+aqum4YQmvwRYdrsjQ0rQQQ/D4HwrnUL2Y2urIk9p4Y5XWNhlZZ4t/6dNuyeQ2ulZol7VBZhQbdGHrMKT3GJJ9P/YdnL7751rHLfLfj3a6FixbPDEcSpswhqrT0cM/C8wsrSk6dR1XQSet2Im0vnCbOR9E5qrkZo9HWj985f9Oy1kzGnCy95WeVifwBpJ3QeX2KT3Z3nMCTLw6+Oi8P28x8+2c7+9gMmTLnUnVxMzBckXUGW7UhChtjmDtFaDKRiy9sYXNaRnMtI7bWpddP/btPLXcsUxAx2CEek1gF+qkC/ERytumqKVH11z85FIld+W4lEvGLRvj2pXVWui9l+xr8aWk4Y2UMkIdeZMIAwsUI+uLMcRFWae7U0IsdrjvCbPeIrTWq4534ZH+4hgkjYk9512hLuA0jLaQcDN16c9wMaKr845d6T545P8zfvfjavicO+INBLWWIU7HwsAw299C9QWyWwbJw5CSOnWa2RTKRG75+b3SRSyNmacTW2h9PPPf7w0r93KXzW5lx0XDkqNMCICTHR9ZPz4MhaQVPJqZqktjdFfrez9tH8pQw6jjeu2rFTc3y6f4MfLIb0obhZNhgXoujP4nDJ1kkwRmRX9N6fau/+ZT79ItvjfSpZRidy3Jsa+e+g8f7fCuWLq6VopZlxp26tBsKyYnhxjpFcIkf759qOUxntV95+L2+yJBPzBoUlmUcvcC2tE3imZP9GdQHSLlixMkHLIslnL2Io6eZYUEiClQ37+xf/pV/e+/YuydGejkeRh+3njp99qW3umdcv2JOM3P0vrQTiNl1Gjf88uBPLhgcDOcS9Zf0kN+n/vOr9ut7j4+iJn19EV2etm6+m0kldIs1hYfxT8nywQCHaeHoaZy5yEmQwsFqb/zZ/inff2RvMj6SbE4pxtR5TSbiz+84bIfmLF8wVTZ7TIeiVp0jpLCS5nxY3iNu+Dr76zXJab9Q/71fHBLuKL3fkc7emfOWzavp6U/ZnKE2dKWVj7k1WdF+HOpk0QTjRH6fv0dr+8avjedebSd3TJ3XsaZaSDhvdRw90MWXLl3WpMYsI5mwwgk7EFJ0VboCR47g70ZqLUeYvP6rj1y41Dey5l9aD/dAp75+9Y1V7tlomlX74fddllkJRDjTjSNnmGlBAgVqWndEl3714WMnOk+OvhqF4iuB890XXth7rmX2iuunqK7ek3HkSCascCekmkN6Z4bTsWBvSgoElH/dob2yp3OMdcjo6dPx6s3L6p1UbyLDmqsgDXVxHIaNd07iTA8jQSpnVLfox+0t//iLPelkZcLwig26ZPT0SzuP9MszVy6aqVldluVEjYDh8CrNlAYKAkNMlzojiipnDvRO/u5jJ12nAhNMzl/oleuuXzU1nUzoloOmmsG+xHEphoOdLJZgnCjgD5z3tf3Nr1Mv7dhPojIDRajwyCuJw0dP7OmkhUtWTQpGLD2SsNRYRgkqtk8pinsYbAdHLjLbMS25+euPGT2XRu8cynD4RN/Cm5ZNVbqiCfKpqCoWWQZBONHFjp1llg0Z5K+d/Fp0ydf+/cjp06crVQEPlZ9+0dvb+7td56qmrVw0Q6X0qYzt9qXBmQj7Csv4Tvay3hQF/P5/293y0q5KTot0Xftwl7tx1Vw10xVNsoYqaJq3bAO6gYOd7HwfANIkJuoX/6i98YeP7zbSiSsWO1KMy2Qh28rs2HOsy565csnsoHPaMs1omqUtVPkgK+hLoLOXaRIO9c9+6JdnnUo0/2Ik4vE+0brhBl8mEUtnWEs9uISLEXScYHEdEigYCp1V277+ZPy1nW+P07+FG7+JmPRe55nfH8W8m9ZPr07YeiJlskiKSRyne5ltk6O2fuMJ58LFykx7KcPJsxfrpy++qSHSnzCFYLEEjp5ljoDCyF8/7YWLi/7m3w+eO3duPE7tYXynDff3x17YdU5qWr1kTg1Lnzcs9CWY7SDg9/3f/a0v7By/WdHU0RlbvWJJI85GkoimGAP5ZG7XLf2nndU//tVu0xjHya24CpPcXcfae+D48cSU5cuWVotuy7RUiR1NXvfQ4+ccp8Izn4phW8a7vcqWldOYfoERgqGqTrnta4/3vrn3ICqUdL8MrsZKFwBnz51/9VBm+oL1cxuMjKt+8zfifE/Fxr6GQl8kmvHNXD9HSP7a57pv/NvtHd3dw82N/SGBy75P/cmW//rA6qv1xAwwSf2fn77z3ttXXBP/FGiccTXW2L1/p5vABCYwgQlMYAL/2fH/AdkCEQl+/Ar/AAAAAElFTkSuQmCCCg==', +// icon4: 'jqtouch4.png', +// startupScreen: null, // Pass a string path to a 320px x 460px startup screen for full screen apps. + statusBar: 'black-translucent', // Styles the status bar when running as a fullscreen app. Other options are `default`, `black`, and `black-translucent`. +// addGlossToIcon: true, // Set to 'false' to prevent automatic glossy button effect on icon. + preloadImages: false, // Pass an array of image paths to load them before page loads. Ex: `['images/link_over.png', 'images/link_select.png']` + fixedViewport: true, // Removes the user's ability to scale the page. Ensures the site behaves more like an application. +// fullScreen: true, // The website will become a fullscreen application when saved to a user's home screen. Set to `false` to disable. +// fullScreenClass: 'fullscreen' // Adds a class to the `<body>` when running in full-screen mode, to allow for easy detection and styling. Set to `false` to disable. +// themeSelectionSelector: '#jqt #themes ul', // ??? + +// useAnimations: true, // Set to `false` to disable all animations. +// useFastTouch: true, // Removes ~350ms onClick delay when tapping a link (use in conjunction with the .tap() event) **Experimental** +// useTouchScroll: true, // Adds support for iOS5 scrolling. Set to false to disable. **Experimental** + + cacheGetRequests: false, // Automatically caches GET requests, so subsequent taps reference the pre-loaded views. (default: true) + +// backSelector: '.back, .cancel, .goback', // A CSS selector for back links/buttons. When clicked, the page history goes back one, automatically reversing whichever entrance animation was used. + +// cubeSelector: '.cube', // Link selector for a cube animation. +// dissolveSelector: '.dissolve', // Link selector for a dissolve animation. +// fadeSelector: '.fade', // Link selector for a fade animation. +// flipSelector: '.flip', // Link selector for a 3d flip animation. + formSelector: null, // Sets which forms are automatically submitted via Ajax. (default: 'form') +// popSelector: '.pop', // Link selector for a pop animation. (default: '.pop') +// slideSelector: 'body > * > ul li a', // Link selector for the default slide-left transition. By default applies to all links within an unordered list. Accepts any jQuery-capable selector `'li > a, a:not(.dontslide)'`, etc. (default: 'body > * > ul li a') +// slideupSelector: '.slideup', // Link selector for a slide up animation. (default: '.slideup') +// submitSelector: '.submit', // Selector which, when clicked, will submit its parent form (and close keyboard if open). (default: '.submit') +// swapSelector: '.swap', // Link selector for 3d swap animation. (default: '.swap') +// touchSelector: 'a, .touch', // Selector for items which are automatically given expanded touch events. This makes ordinary links more responsive and provides trigger events like `swipe` (default: 'a, .touch') + + debug: false + }); + + this.setJQTouch(jqt); + }, + + //========================================================================= + + 'showAddToHomeScreenBaloon': function () { +console.log(">>> showAddToHomeScreenBaloon"); + }, + + //------------------------------------------------------------------------- + + 'selectInitialProxy': function () { +//console.log(">>> selectInitialProxy"); + if (this.isOnline()) { +//console.log("--- selectInitialProxy: using default proxy"); + this._proxy = Clipperz.PM.Proxy.defaultProxy; + } else { + if (this.hasLocalData()) { +//console.log("--- selectInitialProxy: using local cache proxy"); + this._proxy = new Clipperz.PM.Proxy.OfflineCache({'shouldPayTolls':false}); + } else { + this.showOfflineError(); + } + } + }, + + //------------------------------------------------------------------------- + + 'showLoginForm': function (args) { + args = args || {}; + + args['callback'] = MochiKit.Base.method(this, 'doLogin'); + + if (Clipperz.PM.PIN.isSet()) { + args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedPinLogin'); + this.loginForm().showPinLogin(args); + } else { + args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedCredentialsLogin'); + this.loginForm().showCredentialsLogin(args); + } + }, + + //......................................................................... + + 'handleFailedCredentialsLogin': function () { +console.log("LOGIN FAILED"); + this.showLoginForm({'previousFailedAttempt':'LOGIN'}); + }, + + //......................................................................... + + 'handleFailedPinLogin': function () { + var failedAttempts; + var status; + + failedAttempts = Clipperz.PM.PIN.recordFailedAttempt(); + this.showLoginForm({'previousFailedAttempt':'PIN', 'failedAttempts': failedAttempts}); + }, + + //------------------------------------------------------------------------- + + 'doLogin': function (someArgs) { + var deferredResult; + var credentials; + var errorCallback; + var user; + var getPassphraseDelegate; + +//console.log(">>> MainController.doLogin", someArgs); + credentials = someArgs['credentials']; + errorCallback = someArgs['errorCallback'] || MochiKit.Base.noop; + + getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase); + user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate}); + + deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false}); + deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4}); + deferredResult.addCallback(MochiKit.Async.wait, 0.1); + deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection'); + deferredResult.addMethod(user, 'login'); + deferredResult.addCallbacks( + MochiKit.Base.method(this, 'processSuccessfulLogin', user), + errorCallback + ); + deferredResult.callback(); + + return deferredResult; + }, + + //.......................................................................... + + 'processSuccessfulLogin': function (aUser) { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred('MainController.processSuccessfulLogin', {trace:false}); + deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount'); +// deferredResult.addMethod(this, 'removeLoginForm'); + deferredResult.addMethod(this, 'setUser', aUser); + deferredResult.addMethod(this, 'setupApplication'); + deferredResult.addMethod(this, 'runApplication'); + deferredResult.callback(); + + return deferredResult; + }, + + //------------------------------------------------------------------------- + + 'setupApplication': function () { + var deferredResult; + +console.log(">>> setupApplication"); + deferredResult = new Clipperz.Async.Deferred("MainController.setupApplication", {trace:false}); + deferredResult.addMethod(this, 'welcomeFirstTimeUser'); + deferredResult.addMethod(this, 'showPaymentReminder'); + deferredResult.addMethod(this, 'copyDataLocally'); + deferredResult.callback(arguments); + + return deferredResult; + }, + + + //.......................................................................... + + 'isFirstTimeUser': function () { + return false; + }, + + 'welcomeFirstTimeUser': function () { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred('MainController.welcomeFirstTimeUser', {trace:false}); + + if (this.isFirstTimeUser()) { + deferredResult.addCallback(function () { console.log("--> welcome"); }); + } + deferredResult.callback(); + + return deferredResult; + }, + + //.......................................................................... + + 'shouldShowPaymentReminder': function () { + return true; + }, + + 'showPaymentReminder': function () { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred('MainController.showPaymentReminder', {trace:false}); + + if (this.shouldShowPaymentReminder()) { + deferredResult.addCallback(function () { console.log("--> payment reminder"); }); + } + deferredResult.callback(); + + return deferredResult; + }, + + //.......................................................................... + + 'canCopyDataLocally': function () { + return false; + }, + + 'copyDataLocally': function () { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred('MainController.copyDataLocally', {trace:false}); + + if (this.canCopyDataLocally()) { + deferredResult.addCallback(function () { console.log("--> copy data locally"); }); + } + deferredResult.callback(); + + return deferredResult; + + }, + + //------------------------------------------------------------------------- + + 'runApplication': function () { + var deferredResult; + +//console.log(">>> runApplication"); + deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:true}); + deferredResult.addMethod(this.user(), 'getRecords'); + deferredResult.addMethod(this, 'showCards'); + deferredResult.callback(); + + return deferredResult; + }, + + //========================================================================= + + 'showOfflineError': function (anException) { + alert("Error: " + anException); + throw anException; + }, + + //========================================================================= + + 'isOnline': function() { + return navigator.onLine; + }, + + 'hasLocalData': function() { + return false; + }, + + //========================================================================= + + 'loginForm': function() { + if (this._loginForm == null) { + this._loginForm = new Clipperz.PM.UI.Mobile.Components.LoginForm({element:MochiKit.DOM.getElement('loginForm')}); + } + + return this._loginForm; + }, + + 'removeLoginForm': function () { + if (this._loginForm != null) { + this._loginForm.remove(); + this._loginForm = null; + } + }, + + //------------------------------------------------------------------------- + + 'cardList': function () { + if (this._cardList == null) { + this._cardList = new Clipperz.PM.UI.Mobile.Components.CardList({element:MochiKit.DOM.getElement('cardList')}); + MochiKit.Signal.connect(this._cardList, 'selectedCard', this, 'selectCardHandler'); + } + + return this._cardList; + }, + + 'showCards': function (someCards) { + this.cardList().showCards(someCards); + this.jQTouch().goTo('#cardList', 'slideleft'); + }, + + //------------------------------------------------------------------------- + + 'cardDetail': function () { + if (this._cardDetail == null) { + this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:MochiKit.DOM.getElement('cardDetail')}); + } + + return this._cardDetail; + }, + + 'selectCardHandler': function (aCardReference) { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred("MainController.selectCardHandler", {trace:true}); + deferredResult.addMethod(this.cardDetail(), 'render'); + deferredResult.addMethod(this.jQTouch(), 'goTo', '#cardDetail', 'slideleft'); + deferredResult.addMethod(this.user(), 'getRecord', aCardReference); + deferredResult.addMethod(this.cardDetail(), 'showCard'); + deferredResult.callback(); + + return deferredResult; + }, + + //========================================================================= + __syntaxFix__: "syntax fix" +}); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js index ee6d7a3..d6b0574 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js @@ -28,32 +28,36 @@ Clipperz.Base.module('Clipperz.PM.UI.Web.Components'); Clipperz.PM.UI.Web.Components.AccountPanel = function(args) { args = args || {}; Clipperz.PM.UI.Web.Components.AccountPanel.superclass.constructor.apply(this, arguments); // this._initiallySelectedTab = args.selected || 'ACCOUNT'; this._initiallySelectedTab = args.selected || 'PASSPHRASE'; this._tabPanelControllerConfiguration = { // 'ACCOUNT': { // tab: 'accountTab', // panel: 'accountPanel' // }, 'PASSPHRASE': { tab: 'passphraseTab', panel: 'passphrasePanel' }, + 'OTP': { + tab: 'OTPTab', + panel: 'OTPPanel' + }, 'PREFERENCES': { tab: 'preferencesTab', panel: 'preferencesPanel' }, 'LOGIN_HISTORY': { tab: 'loginHistoryTab', panel: 'loginHistoryPanel' } }; return this; } //============================================================================= Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.Common.Components.TabPanelComponent, { @@ -61,32 +65,33 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI. //------------------------------------------------------------------------- 'toString': function () { return "Clipperz.PM.UI.Web.Components.AccountPanel component"; }, //------------------------------------------------------------------------- 'renderSelf': function(/*aContainer, aPosition*/) { //Clipperz.log("AccountPanel.renderSelf element", this.element()); this.append(this.element(), [ {tag:'div', cls:'header', children:[ {tag:'div', cls:'subPanelTabs', children:[ {tag:'ul', children:[ // {tag:'li', id:this.getId('accountTab'), children:[{tag:'a', href:'#', html:'Account'}], cls:'first'}, {tag:'li', id:this.getId('passphraseTab'), children:[{tag:'a', href:'#', html:'Passphrase'}], cls:'first'}, + {tag:'li', id:this.getId('OTPTab'), children:[{tag:'a', href:'#', html:'One Time Passwords'}]}, {tag:'li', id:this.getId('preferencesTab'), children:[{tag:'a', href:'#', html:'Preferences'}]}, {tag:'li', id:this.getId('loginHistoryTab'), children:[{tag:'a', href:'#', html:'Login history'}]} ]} ]} ]}, {tag:'div', cls:'body', children:[ {tag:'div', cls:'accountPanel', children:[ {tag:'div', cls:'subPanelContent', children:[ {tag:'ul', children:[ // {tag:'li', id:this.getId('accountPanel'), children:[ // {tag:'h3', html:"-- Account --"} // ]}, {tag:'li', id:this.getId('passphrasePanel'), children:[ {tag:'h3', cls:'changePassphrase', html:"Change passphrase"}, {tag:'form', id:this.getId('changePassphrase'), cls:'changePassphrase', children:[ {tag:'div', cls:'currentCredentials', children:[ @@ -104,42 +109,42 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI. {tag:'label', html:"new passphrase", 'for':this.getId('newPassphrase')}, {tag:'input', id:this.getId('newPassphrase')} ]}, {tag:'div', cls:'field', children:[ {tag:'label', html:"re-new passphrase", 'for':this.getId('reNewPassphrase')}, {tag:'input', id:this.getId('reNewPassphrase')} ]} ]}, {tag:'div', cls:'confirm', children:[ {tag:'input', type:'checkbox', id:this.getId('confirm')}, {tag:'label', html:"I understand that Clipperz will not be able to recover a lost passphrase", 'for':this.getId('confirm')} ]} ]}, {tag:'div', cls:'clear'}, {tag:'div', cls:'confirmButton', id:this.getId('confirmationButton'), children:[ {tag:'span', html:"change passphrase"} - ]}, - - {tag:'h3', cls:'manageOTP', html:"Manage One-Time Passphrases"}, - {} + ]} + ]}, + {tag:'li', id:this.getId('OTPPanel'), children:[ +// {tag:'h3', html:"Manage One-Time Passphrases"} ]}, {tag:'li', id:this.getId('preferencesPanel'), children:[ - {tag:'h3', html:"-- Preferences --"} +// {tag:'h3', html:"-- Preferences --"} ]}, {tag:'li', id:this.getId('loginHistoryPanel'), children:[ - {tag:'h3', html:"-- Login History --"} +// {tag:'h3', html:"-- Login History --"} ]} ]} ]} ]} ]}, {tag:'div', cls:'footer'} ]); this.tabPanelController().setup({selected:this.initiallySelectedTab()}); }, //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js index d2f1045..462d864 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js @@ -69,40 +69,48 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.DataPanel, Clipperz.PM.UI.Com this.append(this.element(), [ {tag:'div', cls:'header', children:[ {tag:'div', cls:'subPanelTabs', children:[ {tag:'ul', children:[ {tag:'li', id:this.getId('offlineCopyTab'), children:[{tag:'a', href:'#', html:'Offline copy'}], cls:'first'}, {tag:'li', id:this.getId('sharingTab'), children:[{tag:'a', href:'#', html:'Sharing'}]}, {tag:'li', id:this.getId('importTab'), children:[{tag:'a', href:'#', html:'Import'}]}, {tag:'li', id:this.getId('exportTab'), children:[{tag:'a', href:'#', html:'Export'}]} ]} ]} ]}, {tag:'div', cls:'body', children:[ {tag:'div', cls:'accountPanel', children:[ {tag:'div', cls:'subPanelContent', children:[ {tag:'ul', children:[ {tag:'li', id:this.getId('offlineCopyPanel'), children:[ - {tag:'h3', html:"Offline copy"} +// {tag:'h3', html:"Offline copy"}, + {tag:'p', html:"With just one click you can dump all your encrypted data from Clipperz servers to your hard disk and create a read-only offline version of Clipperz to be used when you are not connected to the Internet."}, + {tag:'a', id:this.getId('offlineCopyDownloadLink'), href:'#', html:"Download", cls:'downloadOfflineCopy'} ]}, {tag:'li', id:this.getId('sharingPanel'), children:[ - {tag:'h3', html:"Sharing"} +// {tag:'h3', html:"Sharing"} ]}, {tag:'li', id:this.getId('importPanel'), children:[ - {tag:'h3', html:"Import"} +// {tag:'h3', html:"Import"} ]}, {tag:'li', id:this.getId('exportPanel'), children:[ - {tag:'h3', html:"Export"} +// {tag:'h3', html:"Export"} ]} ]} ]} ]} ]}, {tag:'div', cls:'footer'} ]); this.tabPanelController().setup({selected:this.initiallySelectedTab()}); + MochiKit.Signal.connect(this.getId('offlineCopyDownloadLink'), 'onclick', this, 'downloadOfflineCopy'); }, - + + 'downloadOfflineCopy': function (anEvent) { + anEvent.preventDefault(); + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', anEvent.src()); + }, + //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js index 5d082b5..26506e7 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js @@ -69,72 +69,76 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI //console.log(">> LoginProgress.renderSelf", this.element()); this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress', children: [ // this.append(loginProgressElement, [ {tag:'div', cls:'header', children:[ {tag:'h3', id:this.getId('title'), html:"login progress"} ]}, {tag:'div', cls:'body', children:[ {tag:'div', id:this.getId('progressBar')}, {tag:'div', id:this.getId('errorBox'), cls:'errorBox', children:[ // {tag:'div', cls:'img ALERT', children:[{tag:'div'}]}, {tag:'div', cls:'img ALERT', children:[{tag:'canvas', id:this.getId('canvas')}]}, {tag:'p', html:"Login failed"} ]} ]}, {tag:'div', cls:'footer', children:[ {tag:'div', cls:'buttonArea', id:this.getId('buttonArea'), children:[ - {tag:'div', cls:'button', id:this.getId('button'), children:[ - {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"} - ]} +// {tag:'div', cls:'button', id:this.getId('button'), children:[ +// {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"} +// ]} + {tag:'a', cls:'button', id:this.getId('button'), html:"cancel"} ]} ]} ]}); // ]); Clipperz.PM.UI.Canvas.marks['!'](this.getElement('canvas'), "#ffffff"); this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')})); MochiKit.Style.hideElement(this.getElement('errorBox')); - MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler'); +// MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler'); + MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'cancelEventHandler'); }, //------------------------------------------------------------------------- 'displayElement': function() { return MochiKit.DOM.getElement('loginProgress'); }, //------------------------------------------------------------------------- 'cancelEventHandler': function(anEvent) { anEvent.preventDefault(); MochiKit.Signal.signal(this, 'cancelEvent'); }, //------------------------------------------------------------------------- 'disableCancel': function() { MochiKit.Style.hideElement(this.getElement('buttonArea')); }, //------------------------------------------------------------------------- 'showErrorMessage': function() { - this.getElement('buttonLink').innerHTML = "close"; +// this.getElement('buttonLink').innerHTML = "close"; + this.getElement('button').innerHTML = "close"; + MochiKit.DOM.addElementClass(this.getElement('button'), 'default'); MochiKit.Style.hideElement(this.getElement('progressBar')); this.getElement('title').innerHTML = "Error"; MochiKit.Style.showElement(this.getElement('errorBox')); MochiKit.Style.showElement(this.getElement('buttonArea')); }, //------------------------------------------------------------------------- 'deferredHideModalAndRemove': function(someParameters, aResult) { var deferredResult; deferredResult = new Clipperz.Async.Deferred("LoginProgress.deferredHideModalAndRemove", {trace:false}); deferredResult.addMethod(this, 'deferredHideModal'); deferredResult.addMethod(this, 'remove'); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js index a25c8f5..dcf506f 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js @@ -36,33 +36,32 @@ Clipperz.PM.UI.Web.Components.PageFooter = function(args) { //============================================================================= Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.PageFooter, Clipperz.PM.UI.Common.Components.BaseComponent, { //------------------------------------------------------------------------- 'toString': function () { return "Clipperz.PM.UI.Web.Components.PageFooter component"; }, //------------------------------------------------------------------------- 'renderSelf': function(/*aContainer, aPosition*/) { this.append(this.element(), [ {tag:'div', cls:'footerWrapper', children:[ {tag:'div', cls:'footerContent', children:[ -// {tag:'div', cls:'footerStarIcon'}, {tag:'canvas', id:this.getId('footerStarIcon'), cls:'footerStarIcon'}, - {tag:'span', cls:'copyright', html:'Copyright © 2009-2012 Clipperz Srl'}, + {tag:'span', cls:'copyright', html:'Copyright © 2009-2013 Clipperz Srl'}, {tag:'a', href:'http://www.clipperz.com/terms_of_service', target:'_blank', html:'terms of service'}, {tag:'a', href:'http://www.clipperz.com/privacy_policy', target:'_blank', html:'privacy policy'}, - {tag:'span', cls:'applicationVersion', html:'application version: [1992]'} + {tag:'div', cls:'applicationVersion', htmlString:'application version: <a href="https://github.com/clipperz/password-manager/tree/' + Clipperz_version + '" target="github">' + Clipperz_version + '</a>'} ]} ]} ]); Clipperz.PM.UI.Canvas.star.normal(this.getElement('footerStarIcon'), "#7e7e7e"); }, //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js index 3ee6189..0fa369f 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js @@ -69,42 +69,42 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.ToolsPanel, Clipperz.PM.UI.Co this.append(this.element(), [ {tag:'div', cls:'header', children:[ {tag:'div', cls:'subPanelTabs', children:[ {tag:'ul', children:[ {tag:'li', id:this.getId('passwordGeneratorTab'), children:[{tag:'a', href:'#', html:'Password generator'}], cls:'first'}, {tag:'li', id:this.getId('bookmarkletTab'), children:[{tag:'a', href:'#', html:'Bookmarklet'}]}, {tag:'li', id:this.getId('compactEditionTab'), children:[{tag:'a', href:'#', html:'Compact edition'}]}, {tag:'li', id:this.getId('httpAuthTab'), children:[{tag:'a', href:'#', html:'HTTP Auth'}]} ]} ]} ]}, {tag:'div', cls:'body', children:[ {tag:'div', cls:'accountPanel', children:[ {tag:'div', cls:'subPanelContent', children:[ {tag:'ul', children:[ {tag:'li', id:this.getId('passwordGeneratorPanel'), children:[ - {tag:'h3', html:"Password generator"} +// {tag:'h3', html:"Password generator"} ]}, {tag:'li', id:this.getId('bookmarkletPanel'), children:[ - {tag:'h3', html:"Bookmarklet"} +// {tag:'h3', html:"Bookmarklet"} ]}, {tag:'li', id:this.getId('compactEditionPanel'), children:[ - {tag:'h3', html:"Compact edition"} +// {tag:'h3', html:"Compact edition"} ]}, {tag:'li', id:this.getId('httpAuthPanel'), children:[ - {tag:'h3', html:"HTTP Auth"} +// {tag:'h3', html:"HTTP Auth"} ]} ]} ]} ]} ]}, {tag:'div', cls:'footer'} ]); this.tabPanelController().setup({selected:this.initiallySelectedTab()}); }, //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js index 9a0e744..1ab2e69 100644 --- a/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js +++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js @@ -218,35 +218,37 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, { user = args.user; this.setUser(user); slot.setContent(this.appPage()); this.appPage().slotNamed('userInfoBox').setContent(this.userInfoBox()); this.appPage().slotNamed('tabSidePanel').setContent(this.tabSidePanel()); this.appPage().slotNamed('accountPanel').setContent(this.accountPanel()); this.appPage().slotNamed('dataPanel').setContent(this.dataPanel()); this.appPage().slotNamed('toolsPanel').setContent(this.toolsPanel()); this.hideAllAppPageTabSlots(); this.appPage().showSlot(this.slotNameForTab('cards')); - MochiKit.Signal.connect(this.tabSidePanel(), 'tabSelected', this, 'handleTabSelected'); - MochiKit.Signal.connect(this.tabSidePanel(), 'addCard', this, 'handleAddCard'); - MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'addCard', this, 'handleAddCard'); + MochiKit.Signal.connect(this.tabSidePanel(), 'tabSelected', this, 'handleTabSelected'); + MochiKit.Signal.connect(this.tabSidePanel(), 'addCard', this, 'handleAddCard'); + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'addCard', this, 'handleAddCard'); + + MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', this, 'handleDownloadOfflineCopy'); deferredResult = new Clipperz.Async.Deferred("AppController.run", {trace:false}); deferredResult.addMethod(this.cardsController(), 'run', {slot:this.appPage().slotNamed('cardGrid'), user:user}); // deferredResult.addMethod(this.directLoginsController(), 'run', {slot:this.appPage().slotNamed('directLoginGrid'), user:user}); deferredResult.addMethod(this, 'populateUserInfo'); deferredResult.addCallback(MochiKit.Visual.ScrollTo, 'miscLinks', {duration:0}); deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'CARDS_CONTROLLER_DID_RUN'); deferredResult.addMethod(this.tabSidePanel(), 'selectTab', 'cards'); deferredResult.callback(); }, //----------------------------------------------------------------------------- 'handleTabSelected': function (selectedTabName) { @@ -308,19 +310,42 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, { MochiKit.Base.method(this.user(), 'lock') ], {trace:false}); }, //............................................................................. 'handleUnlock': function (anEvent) { return Clipperz.Async.callbacks("AppController.handleUnock", [ MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress'), MochiKit.Base.method(this.user(), 'login'), MochiKit.Base.method(this.cardsController(), 'focus'), MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'progressDone'), MochiKit.Base.method(this.userInfoBox(), 'unlock') ], {trace:false}); }, + 'handleDownloadOfflineCopy': function (anEvent) { +console.log("AppController.handleDownloadOfflineCopy"); + var downloadHref; + + downloadHref = window.location.href.replace(/\/[^\/]*$/,'') + Clipperz_dumpUrl; + + if (Clipperz_IEisBroken == true) { + window.open(downloadHref, ""); + } else { + var deferredResult; + var newWindow; + + newWindow = window.open("", ""); + + deferredResult = new Clipperz.Async.Deferred("AppController.handleDownloadOfflineCopy", {trace:true}); + deferredResult.addCallback(MochiKit.Base.method(this.user().connection(), 'message'), 'echo', {'echo':"echo"}); + deferredResult.addCallback(function(aWindow) { + aWindow.location.href = downloadHref; + }, newWindow); + deferredResult.callback(); + } + }, + //============================================================================= __syntaxFix__: "syntax fix" }); diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js deleted file mode 100644 index 5380aa1..0000000 --- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js +++ b/dev/null @@ -1,163 +0,0 @@ -/* - -Copyright 2008-2011 Clipperz Srl - -This file is part of Clipperz Community Edition. -Clipperz Community Edition is an online password manager. -For further information about its features and functionalities please -refer to http://www.clipperz.com. - -* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see - <http://www.gnu.org/licenses/>. - -*/ - -Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components'); - -Clipperz.PM.UI.iPhone.Components.CardDetail = function(args) { - args = args || {}; - - Clipperz.PM.UI.iPhone.Components.CardDetail.superclass.constructor.apply(this, arguments); - - this._cardReference = null; - - return this; -} - -//============================================================================= - -Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, { - - //------------------------------------------------------------------------- - - 'toString': function () { - return "Clipperz.PM.UI.iPhone.Components.CardDetail component"; - }, - - //------------------------------------------------------------------------- - - 'cardReference': function () { - return this._cardReference; - }, - - 'setCardReference': function (aValue) { - this._cardReference = aValue; - }, - - //------------------------------------------------------------------------- - - 'renderSelf': function(/*aContainer, aPosition*/) { - this.append(this.element(), [ - {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[ - {tag:'div', id:this.getId('progressBar')} //, -// {tag:'h1', cls:'loading', html:"loading"} - ]} - ]); - - this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')})); - MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0); - }, - - //========================================================================= - - 'showCardDetails': function (someData) { - this.element().innerHTML = ''; - this.append(this.element(), [ - {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) { - return {tag:'div', cls:'row', children:[ - {tag:'label', html:aFieldData['label']}, -// {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), html:aFieldData['value']} - {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), children:[ - {tag:'div', children:[{tag:'p', html:aFieldData['value']}]} - ]} -// {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), value:aFieldData['value'], disabled:true} - - ]} - }, someData['fields'])} - ]); - - MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) { - MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); }) - }, this)); - - if (someData['directLogins'].length > 0) { - this.append(this.element(), [ - {tag:'h2', html:"Direct logins"}, - {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) { - return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[ - {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']}, -// {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']} - {tag:'span', cls:'directLogin', html:aDirectLoginData['label']} - ]} - }, someData['directLogins'])} - ]); - - MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) { - MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler'); - }, this), - MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row']) - ) - }; - - if (someData['notes'] != '') { - this.append(this.element(), [ - {tag:'h2', html:"Notes"}, - {tag:'fieldset', id:this.getId('fieldset'), children:[ - {tag:'div', cls:'row notes', children:[ - {tag:'span', html:someData['notes']} - ]} - ]} - ]); - }; - - return true; - }, - - //------------------------------------------------------------------------- -/* - 'toggleClickHandler': function (anEvent) { - var nextState; - var fieldValue; - -//console.log("TOGGLE"); - anEvent.preventDefault; - fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0]; - - nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true'); - if (nextState) { - MochiKit.DOM.removeElementClass(fieldValue, 'clear'); - } else { - MochiKit.DOM.addElementClass(fieldValue, 'clear'); - } - - MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState); - }, -*/ - //========================================================================= - - 'directLoginClickHandler': function (anEvent) { - anEvent.preventDefault(); - - if (/(directLogin_)/.test(anEvent.src().id)) { - var directLoginReference; - - directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2]; - MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference}); - } - }, - - //========================================================================= - - __syntaxFix__: "syntax fix" -}); diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js deleted file mode 100644 index 5341878..0000000 --- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js +++ b/dev/null @@ -1,178 +0,0 @@ -/* - -Copyright 2008-2011 Clipperz Srl - -This file is part of Clipperz Community Edition. -Clipperz Community Edition is an online password manager. -For further information about its features and functionalities please -refer to http://www.clipperz.com. - -* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see - <http://www.gnu.org/licenses/>. - -*/ - -Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components'); - -Clipperz.PM.UI.iPhone.Components.LoginForm = function(args) { - args = args || {}; - - Clipperz.PM.UI.iPhone.Components.LoginForm.superclass.constructor.apply(this, arguments); - - return this; -} - -//============================================================================= - -Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, { - - //------------------------------------------------------------------------- - - 'toString': function () { - return "Clipperz.PM.UI.iPhone.Components.LoginForm component"; - }, - - //------------------------------------------------------------------------- - - 'focusOnUsername': function () { - this.getElement('username').focus(); - }, - - //------------------------------------------------------------------------- - - 'username': function () { - return this.getElement('username').value; - }, - - 'passphrase': function () { - return this.getElement('passphrase').value; - }, - - //------------------------------------------------------------------------- - - 'renderSelf': function(/*aContainer, aPosition*/) { - this.append(this.element(), [ - {tag:'div', cls:'toolbar iPhoneClipperzToolbar', children:[ - {tag:'h1', id:'pageTitle', html:'Clipperz'}, - {tag:'a', id:'backButton', cls:'button', href:'#', html:"back"} - ]}, - {tag:'form', title:'Theaters', cls:'panel toolbarlessPanel loginForm', id:this.getId('loginFormPanel'), children:[ - {tag:'fieldset', id:this.getId('fieldset'), children:[ - {tag:'div', cls:'row', children:[ - {tag:'label', html:"username"}, - {tag:'input', type:'text', name:'username', value:"", autocorrect:'off', autocapitalize:'off', id:this.getId('username')} - ]}, - {tag:'div', cls:'row', children:[ - {tag:'label', html:"passphrase"}, - {tag:'input', type:'password', name:'passphrase', value:"", id:this.getId('passphrase')} - ]} - ]}, - {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Login", id:this.getId('submit')} - ]}, - {tag:'div', cls:'panel toolbarlessPanel loginProgressPanel', id:this.getId('loginProgressPanel'), children:[ - {tag:'div', id:this.getId('progressBar')} //, -// {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Cancel", id:this.getId('cancel')} - ]}, - {tag:'div', cls:'panel loginErrorPanel', id:this.getId('loginErrorPanel'), children:[ - {tag:'div', cls:'errorMessage', id:this.getId('errorMessageBox'), children:[ - {tag:'h2', id:this.getId('errorMessage'), html:"Login failed"} - ]} - ]} - ]); - - MochiKit.Signal.connect(this.getElement('submit'), 'onclick', this, 'submitHandler'); - MochiKit.Signal.connect(this.getElement('loginFormPanel'), 'onsubmit', this, 'submitHandler'); - -// MochiKit.Signal.connect(this.getElement('cancel'), 'onclick', this, 'cancelHandler'); - MochiKit.Signal.connect('backButton', 'onclick', this, 'backHandler'); - - this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')})); - -// MochiKit.Style.hideElement(this.getElement('errorMessage')); - - this.showLoginForm(); -// MochiKit.Async.callLater(0.2, MochiKit.Base.method(this, 'focusOnUsername')); - }, - - //------------------------------------------------------------------------- - - 'showLoginForm': function () { - MochiKit.Style.showElement(this.getElement('loginFormPanel')); - MochiKit.Style.hideElement(this.getElement('loginProgressPanel')); - MochiKit.Style.hideElement(this.getElement('loginErrorPanel')); - MochiKit.Style.hideElement('backButton'); - }, - - 'slideInLoginForm': function () { - var offset; - - offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480); - - MochiKit.Style.showElement(this.getElement('loginFormPanel')); - MochiKit.Style.setElementPosition(this.getElement('loginFormPanel'), {x:-offset, y:0}); - - new MochiKit.Visual.Sequence([ - new MochiKit.Visual.Parallel([ - new MochiKit.Visual.Move(this.getElement('loginErrorPanel'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}), - new MochiKit.Visual.Move(this.getElement('loginFormPanel'), {x:0, y:0, mode:'absolute', transition:MochiKit.Visual.Transitions.linear, sync:true}), - MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true}) - ], {duration:0.5, sync:true}), - MochiKit.Visual.fade(this.getElement('loginErrorPanel'), {duration:0, sync:true}) - ], {}) - }, - - 'showLoginProgress': function () { - MochiKit.Style.hideElement(this.getElement('loginFormPanel')); - MochiKit.Style.showElement(this.getElement('loginProgressPanel')); - }, - - 'showLoginError': function (anError) { - this.getElement('errorMessage').innerHTML = "Login error"; - - MochiKit.Style.showElement('backButton'); - MochiKit.Style.hideElement(this.getElement('loginProgressPanel')); - MochiKit.Style.showElement(this.getElement('loginErrorPanel')); - MochiKit.Style.setElementPosition(this.getElement('loginErrorPanel'), {x:0, y:45}); - }, - - //------------------------------------------------------------------------- -/* - 'disableCancelButton': function () { - MochiKit.DOM.hideElement(this.getElement('cancel')); - }, -*/ - //------------------------------------------------------------------------- - - 'submitHandler': function (anEvent) { - anEvent.preventDefault(); - - MochiKit.Signal.signal(this, 'doLogin', {'username':this.username(), 'passphrase':this.passphrase()}); - }, - - 'cancelHandler': function (anEvent) { - anEvent.preventDefault(); - -//console.log("CANCEL"); - }, - - 'backHandler': function (anEvent) { - anEvent.preventDefault(); - - this.slideInLoginForm(); - }, - - //------------------------------------------------------------------------- - - __syntaxFix__: "syntax fix" -}); diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js deleted file mode 100644 index 3fcaae1..0000000 --- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js +++ b/dev/null @@ -1,369 +0,0 @@ -/* - -Copyright 2008-2011 Clipperz Srl - -This file is part of Clipperz Community Edition. -Clipperz Community Edition is an online password manager. -For further information about its features and functionalities please -refer to http://www.clipperz.com. - -* Clipperz Community Edition 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 Community Edition 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 Community Edition. If not, see - <http://www.gnu.org/licenses/>. - -*/ - -Clipperz.Base.module('Clipperz.PM.UI.iPhone.Controllers'); - -// Some parts of this controller have been derived from the iUI library. - -Clipperz.PM.UI.iPhone.Controllers.MainController = function() { - this._loginForm = null; - this._cardList = null; - this._cachedValues = null; - this._user = null; - - if (typeof window.onorientationchange == 'object') { - MochiKit.Signal.connect(window, 'onorientationchange', this, 'orientationChangeHandler'); - MochiKit.Async.callLater(0, MochiKit.Base.method(this, 'orientationChangeHandler')); - } else { - this.setOrientation('portrait'); -// this.setOrientation('landscape'); - } - - this.addMetaTag('viewport', 'width=devicewidth; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;'); - this.addMetaTag('apple-mobile-web-app-capable', 'yes'); - this.addMetaTag('apple-mobile-web-app-status-bar-style', 'black'); - - this.addLinkTag('apple-touch-icon', 'data:image/png;charset=utf-8;base64,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'); -// this.addLinkTag('apple-touch-startup-image', 'default.png'); - -// if (!window.navigator.standalone) // not running as an installed app - - MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', this, 'selectedDirectLoginHandler'); - - MochiKit.DOM.addElementClass(document.body, 'iPhone'); - return this; -} - -MochiKit.Base.update(Clipperz.PM.UI.iPhone.Controllers.MainController.prototype, { - - 'toString': function () { - return "Clipperz.PM.UI.iPhone.Controllers.MainController"; - }, - - //========================================================================= - - 'user': function () { - return this._user; - }, - - 'setUser': function (aValue) { - this._user = aValue; - }, - - //========================================================================= - - 'loginForm': function() { - if (this._loginForm == null) { - MochiKit.DOM.removeElement('mainDiv'); - this._loginForm = new Clipperz.PM.UI.iPhone.Components.LoginForm({element:MochiKit.DOM.currentDocument().body}); - MochiKit.Signal.connect(this._loginForm, 'doLogin', this, 'doLoginHandler') - } - - return this._loginForm; - }, - - 'removeLoginForm': function () { - if (this._loginForm != null) { - this._loginForm.remove(); - this._loginForm = null; - } - }, - - //----------------------------------------------------------------------------- - - 'cardList': function () { - if (this._cardList == null) { - this._cardList = new Clipperz.PM.UI.iPhone.Components.CardList({element:MochiKit.DOM.currentDocument().body}); - MochiKit.Signal.connect(this._cardList, 'searchEvent', this, 'searchEventHandler') - MochiKit.Signal.connect(this._cardList, 'selectedCard', this, 'selecetedCardHandler') - } - - return this._cardList; - }, - - //========================================================================= - - 'currentWidth': function () { - return this._currentWidth; - }, - - 'setCurrentWidth': function (aValue) { - this._currentWidth = aValue; - }, - - //========================================================================= - - 'orientationChangeHandler': function () { - switch(window.orientation) { - case 0: - this.setOrientation('portrait'); - break; - case 90: - case -90: - this.setOrientation('landscape'); - break; - } - }, - - //------------------------------------------------------------------------- - - 'setOrientation': function (anOrientation) { - document.body.setAttribute('orientation', anOrientation); - setTimeout(scrollTo, 100, 0, 1); - }, - - //------------------------------------------------------------------------- - - 'slidePages': function (fromPage, toPage, backwards) { - var axis; - var slideDone; - - slideDone = function () { - // console.log("slideDone"); - if (!hasClass(toPage, "dialog")) { - fromPage.removeAttribute("selected"); - } - checkTimer = setInterval(checkOrientAndLocation, 300); - setTimeout(updatePage, 0, toPage, fromPage); - fromPage.removeEventListener('webkitTransitionEnd', slideDone, false); - } - - axis = (backwards ? fromPage : toPage).getAttribute("axis"); - - clearInterval(checkTimer); - - if (canDoSlideAnim() && axis != 'y') { - slide2(fromPage, toPage, backwards, slideDone); - } else { - slide1(fromPage, toPage, backwards, axis, slideDone); - } - }, - - //------------------------------------------------------------------------- - - 'getCachedValues': function () { - var deferredResult; - - if (this._cachedObjects != null) { - deferredResult = MochiKit.Async.succeed(this._cachedObjects); - } else { - deferredResult = new Clipperz.Async.Deferred("MainController.getCachedValues", {trace:false}); - deferredResult.addMethod(this.user(), 'getRecords'); - deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.getCachedValues - collectResults", { - '_rowObject': MochiKit.Async.succeed, - '_reference': MochiKit.Base.methodcaller('reference'), - 'label': MochiKit.Base.methodcaller('label'), - 'favicon': MochiKit.Base.methodcaller('favicon'), - '_searchableContent': MochiKit.Base.methodcaller('searchableContent') - }, {trace:false})); - deferredResult.addCallback(Clipperz.Async.collectAll); - deferredResult.addCallback(MochiKit.Base.bind(function (someRows) { - this._cachedObjects = someRows; - return this._cachedObjects; - }, this)); - deferredResult.callback(); - } - - return deferredResult; - }, - //========================================================================= - - 'run': function(shouldShowRegistrationForm) { - this.loginForm().render(); - MochiKit.Async.callLater(1, MochiKit.Base.method(this.loginForm(), 'focusOnUsername')); - }, - - //========================================================================= - - 'doLoginHandler': function (someArgs) { - var deferredResult; - var parameters; - var shouldUseOTP; -// var loginProgress; - var user; - var getPassphraseDelegate; - - parameters = someArgs; - shouldUseOTP = (typeof(parameters.passphrase) == 'undefined'); - - getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, parameters.passphrase); - user = new Clipperz.PM.DataModel.User({'username':parameters.username, 'getPassphraseFunction':getPassphraseDelegate}); - - deferredResult = new Clipperz.Async.Deferred("MainController.doLogin", {trace:false}); - deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4}); - deferredResult.addMethod(this.loginForm(), 'showLoginProgress'); - deferredResult.addCallback(MochiKit.Async.wait, 0.1); - deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection'); - deferredResult.addMethod(user, 'login'); - deferredResult.addMethod(this, 'setUser', user); - deferredResult.addMethod(user, 'getRecords'); - deferredResult.addMethod(this, 'removeLoginForm'); - deferredResult.addMethod(this.cardList(), 'render'); - deferredResult.addMethod(this, 'displaySelectedRecords', ''); - deferredResult.addErrback(MochiKit.Base.method(this.loginForm(), 'showLoginError')); - deferredResult.callback(); - - return deferredResult; - }, - - //========================================================================= - - 'searchEventHandler': function (aValue) { -//console.log("searching for ... " + aValue); - return this.displaySelectedRecords(aValue); - }, - - //========================================================================= - - '_displaySelectedRows': function (aFilter, someRows) { - var result; - - result = someRows; - - if (aFilter != null) { - var filter; - var filterRegExp; - - filter = aFilter.replace(/[^A-Za-z0-9]/g, "\\$&"); - filterRegExp = new RegExp(filter, "i"); - result = MochiKit.Base.filter(function (aCachedResult) { return filterRegExp.test(aCachedResult['_searchableContent'])}, result); - } - - - result.sort(MochiKit.Base.partial(function (aKey, aComparator, aObject, bObject) { - return aComparator(aObject[aKey], bObject[aKey]); - }, 'label', Clipperz.Base.caseInsensitiveCompare)); - - this.cardList().update(result); - }, - - //------------------------------------------------------------------------- - - 'displaySelectedRecords': function (aFilter) { - return Clipperz.Async.callbacks("MainController.displaySelectedrows", [ - MochiKit.Base.method(this, 'getCachedValues'), - MochiKit.Base.method(this, '_displaySelectedRows', aFilter) - ], {trace:false}); - }, - - //========================================================================= - - 'selecetedCardHandler': function (aRecordReference) { - var deferredResult; - var recordData; - - recordData = {}; -//console.log("Showing detail for card with reference", aRecordReference); - deferredResult = new Clipperz.Async.Deferred("MainController.selectedCardHandler", {trace:false}); - deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); - deferredResult.collectResults({ - '_reference': MochiKit.Base.methodcaller('reference'), - 'title': MochiKit.Base.methodcaller('label'), - 'favicon': MochiKit.Base.methodcaller('favicon') - }); - deferredResult.addCallback(function (someData) { - MochiKit.Base.update(recordData, someData); - }) - deferredResult.addMethod(this.cardList(), 'showCard', recordData); - - deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); - deferredResult.addMethodcaller('notes'); - deferredResult.addCallback(function (someNotes) { - recordData['notes'] = someNotes; - }) - - deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); - deferredResult.addMethodcaller('getCurrentRecordVersion'); - deferredResult.addMethodcaller('fields'); - deferredResult.addCallback(MochiKit.Base.values); - deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - fields", { - 'label': MochiKit.Base.methodcaller('label'), - 'value': MochiKit.Base.methodcaller('value'), - 'isHidden': MochiKit.Base.methodcaller('isHidden') - }, {trace:false})); - deferredResult.addCallback(Clipperz.Async.collectAll); - deferredResult.addCallback(function (someData) { - recordData['fields'] = someData; - }); - - deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); - deferredResult.addMethodcaller('directLogins'); - deferredResult.addCallback(MochiKit.Base.values); - deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - directLogins", { - 'label': MochiKit.Base.methodcaller('label'), - 'favicon': MochiKit.Base.methodcaller('favicon'), - '_reference': MochiKit.Base.methodcaller('reference') - }, {trace:false})); - deferredResult.addCallback(Clipperz.Async.collectAll); - deferredResult.addCallback(function (someData) { - recordData['directLogins'] = someData; - }); - - deferredResult.addMethod(this.cardList(), 'showCardDetails', recordData); - deferredResult.callback(); - - return deferredResult; - }, - - //========================================================================= - - 'selectedDirectLoginHandler': function (someData) { - var deferredResult; - -//console.log("<<< signal - directLogin"); - deferredResult = new Clipperz.Async.Deferred("MainController.selectedDirectLoginHandler", {trace:false}); - deferredResult.addMethod(this.user(), 'getRecord', someData['cardReference']); - deferredResult.addMethodcaller('directLogins'); - deferredResult.addCallback(MochiKit.Base.itemgetter(someData['directLoginReference'])); -// deferredResult.addMethodcaller('runDirectLogin'); - deferredResult.addCallback(Clipperz.PM.UI.Common.Controllers.DirectLoginRunner.openDirectLogin); - deferredResult.callback(); - - return deferredResult; - }, - - //========================================================================= - - 'addMetaTag': function (aName, aContent) { - var metaTag; - - metaTag = document.createElement('meta'); - metaTag.name = aName; - metaTag.content = aContent; - document.getElementsByTagName('head')[0].appendChild(metaTag); - }, - - 'addLinkTag': function (aRel, anHref) { - var linkTag; - - linkTag = document.createElement('link'); - linkTag.rel = aRel; - linkTag.href = anHref; - document.getElementsByTagName('head')[0].appendChild(linkTag); - }, - - //========================================================================= - __syntaxFix__: "syntax fix" -});
\ No newline at end of file |