summaryrefslogtreecommitdiff
path: root/frontend/gamma/js/Clipperz/PM
authorClipperz <info@clipperz.com>2013-01-08 15:21:04 (UTC)
committer Clipperz <info@clipperz.com>2013-01-08 15:21:04 (UTC)
commit816fc35420a434b1d54ae5833d617608f7456048 (patch) (unidiff)
tree95b8caf354083593d7775e4dda6f4c556b44a2d9 /frontend/gamma/js/Clipperz/PM
parent267603e3aa3cf0029411e67ae14018b52344c296 (diff)
downloadclipperz-816fc35420a434b1d54ae5833d617608f7456048.zip
clipperz-816fc35420a434b1d54ae5833d617608f7456048.tar.gz
clipperz-816fc35420a434b1d54ae5833d617608f7456048.tar.bz2
Updated version of /gamma
Tons of changes, included a new draft of the mobile version (still VERY rough)
Diffstat (limited to 'frontend/gamma/js/Clipperz/PM') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/gamma/js/Clipperz/PM/Connection.js34
-rw-r--r--frontend/gamma/js/Clipperz/PM/DataModel/User.js20
-rw-r--r--frontend/gamma/js/Clipperz/PM/PIN.js134
-rw-r--r--frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js65
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js4
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js34
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js34
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js299
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardList.js (renamed from frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardList.js)111
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js356
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js393
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js17
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js18
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js14
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js8
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js31
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js163
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js178
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js369
19 files changed, 1484 insertions, 798 deletions
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
@@ -20,48 +20,49 @@ refer to http://www.clipperz.com.
20* You should have received a copy of the GNU Affero General Public 20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see 21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>. 22 <http://www.gnu.org/licenses/>.
23 23
24*/ 24*/
25 25
26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } 26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } 27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
28 28
29//----------------------------------------------------------------------------- 29//-----------------------------------------------------------------------------
30// 30//
31 // Abstract C O N N E C T I O N class 31 // Abstract C O N N E C T I O N class
32// 32//
33//----------------------------------------------------------------------------- 33//-----------------------------------------------------------------------------
34 34
35Clipperz.PM.Connection = function (args) { 35Clipperz.PM.Connection = function (args) {
36 args = args || {}; 36 args = args || {};
37 37
38 this._proxy = args.proxy || Clipperz.PM.Proxy.defaultProxy; 38 this._proxy = args.proxy || Clipperz.PM.Proxy.defaultProxy;
39 this._getCredentialsFunction = args.getCredentialsFunction; 39 this._getCredentialsFunction = args.getCredentialsFunction;
40 40
41 this._clipperz_pm_crypto_version = null; 41 this._clipperz_pm_crypto_version = null;
42 this._connectionId = null; 42 this._connectionId = null;
43 this._sharedSecret = null; 43 this._sharedSecret = null;
44 this._serverLockValue = null;
44 45
45 return this; 46 return this;
46} 47}
47 48
48Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, { 49Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, {
49 50
50 'toString': function() { 51 'toString': function() {
51 return "Connection [" + this.version() + "]"; 52 return "Connection [" + this.version() + "]";
52 }, 53 },
53 54
54 //========================================================================= 55 //=========================================================================
55 56
56 'version': function() { 57 'version': function() {
57 throw Clipperz.Base.exception.AbstractMethod; 58 throw Clipperz.Base.exception.AbstractMethod;
58 }, 59 },
59 60
60 'clipperz_pm_crypto_version': function() { 61 'clipperz_pm_crypto_version': function() {
61 if (this._clipperz_pm_crypto_version == null) { 62 if (this._clipperz_pm_crypto_version == null) {
62 var connectionVersions; 63 var connectionVersions;
63 varversions; 64 varversions;
64 varversion; 65 varversion;
65 var i, c; 66 var i, c;
66 67
67 version = null; 68 version = null;
@@ -125,48 +126,58 @@ MochiKit.Logging.logError("### Connection.defaultErrorHandler: " + anErrorString
125 'serverSideUserCredentials': function() { 126 'serverSideUserCredentials': function() {
126 throw Clipperz.Base.exception.AbstractMethod; 127 throw Clipperz.Base.exception.AbstractMethod;
127 }, 128 },
128 129
129 //========================================================================= 130 //=========================================================================
130 131
131 'sharedSecret': function () { 132 'sharedSecret': function () {
132 return this._sharedSecret; 133 return this._sharedSecret;
133 }, 134 },
134 135
135 'setSharedSecret': function (aValue) { 136 'setSharedSecret': function (aValue) {
136 this._sharedSecret = aValue; 137 this._sharedSecret = aValue;
137 }, 138 },
138 139
139 //------------------------------------------------------------------------- 140 //-------------------------------------------------------------------------
140 141
141 'connectionId': function() { 142 'connectionId': function() {
142 return this._connectionId; 143 return this._connectionId;
143 }, 144 },
144 145
145 'setConnectionId': function(aValue) { 146 'setConnectionId': function(aValue) {
146 this._connectionId = aValue; 147 this._connectionId = aValue;
147 }, 148 },
148 149
150 //-------------------------------------------------------------------------
151
152 'serverLockValue': function () {
153 return this._serverLockValue;
154 },
155
156 'setServerLockValue': function (aValue) {
157 this._serverLockValue = aValue;
158 },
159
149 //========================================================================= 160 //=========================================================================
150/* 161/*
151 //TODO: ????? 162 //TODO: ?????
152 'oneTimePassword': function() { 163 'oneTimePassword': function() {
153 return this._oneTimePassword; 164 return this._oneTimePassword;
154 }, 165 },
155 166
156 'setOneTimePassword': function(aValue) { 167 'setOneTimePassword': function(aValue) {
157 this._oneTimePassword = aValue; 168 this._oneTimePassword = aValue;
158 }, 169 },
159*/ 170*/
160 //========================================================================= 171 //=========================================================================
161 172
162 'reset': function() { 173 'reset': function() {
163 this.setSharedSecret(null); 174 this.setSharedSecret(null);
164 this.setConnectionId(null); 175 this.setConnectionId(null);
165 }, 176 },
166 177
167 //========================================================================= 178 //=========================================================================
168 __syntaxFix__: "syntax fix" 179 __syntaxFix__: "syntax fix"
169 180
170} 181}
171); 182);
172 183
@@ -299,55 +310,54 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
299 args = { 310 args = {
300 'message': 'oneTimePassword', 311 'message': 'oneTimePassword',
301 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion, 312 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion,
302 'parameters': { 313 'parameters': {
303 'oneTimePasswordKey': Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(someParameters['username'], normalizedOTP), 314 'oneTimePasswordKey': Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(someParameters['username'], normalizedOTP),
304 'oneTimePasswordKeyChecksum':Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(someParameters['username'], normalizedOTP) 315 'oneTimePasswordKeyChecksum':Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(someParameters['username'], normalizedOTP)
305 } 316 }
306 } 317 }
307 318
308 return Clipperz.Async.callbacks("Connction.redeemOTP", [ 319 return Clipperz.Async.callbacks("Connction.redeemOTP", [
309 MochiKit.Base.method(this.proxy(), 'handshake', args), 320 MochiKit.Base.method(this.proxy(), 'handshake', args),
310 function(aResult) { 321 function(aResult) {
311 return Clipperz.PM.Crypto.deferredDecrypt({ 322 return Clipperz.PM.Crypto.deferredDecrypt({
312 value:aResult['data'], 323 value:aResult['data'],
313 key:normalizedOTP, 324 key:normalizedOTP,
314 version:aResult['version'] 325 version:aResult['version']
315 }); 326 });
316 }, 327 },
317 function(aResult) { 328 function(aResult) {
318 return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString(); 329 return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString();
319 } 330 }
320 ], {trace:false}) 331 ], {trace:false})
321 }, 332 },
322 333
323 'login': function(/*anUsername, aPassphrase*/) { 334 'login': function(isReconnecting) {
324 vardeferredResult; 335 vardeferredResult;
325 var cryptoVersion; 336 var cryptoVersion;
326 var srpConnection; 337 var srpConnection;
327 338
328 cryptoVersion = this.clipperz_pm_crypto_version(); 339 cryptoVersion = this.clipperz_pm_crypto_version();
329
330 deferredResult = new Clipperz.Async.Deferred("Connection.login", {trace:false}); 340 deferredResult = new Clipperz.Async.Deferred("Connection.login", {trace:false});
331 deferredResult.addCallback(this.getCredentialsFunction()); 341 deferredResult.addCallback(this.getCredentialsFunction());
332 deferredResult.addMethod(this, 'normalizedCredentials'); 342 deferredResult.addMethod(this, 'normalizedCredentials');
333 // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_sendingCredentials'); 343 // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_sendingCredentials');
334 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); 344 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
335 deferredResult.addCallback(MochiKit.Base.bind(function(someCredentials) { 345 deferredResult.addCallback(MochiKit.Base.bind(function(someCredentials) {
336 srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() }); 346 srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() });
337 }, this)); 347 }, this));
338 deferredResult.addCallback(function() { 348 deferredResult.addCallback(function() {
339 var result; 349 var result;
340 350
341 result = { 351 result = {
342 message: 'connect', 352 message: 'connect',
343 version: cryptoVersion, 353 version: cryptoVersion,
344 parameters: { 354 parameters: {
345 C: srpConnection.C(), 355 C: srpConnection.C(),
346 A: srpConnection.A().asString(16) 356 A: srpConnection.A().asString(16)
347 // reconnecting: this.connectionId() 357 // reconnecting: this.connectionId()
348 } 358 }
349 }; 359 };
350 360
351 // TODO: ????? 361 // TODO: ?????
352 // if (isReconnecting == true) { 362 // if (isReconnecting == true) {
353 // args.parameters['reconnecting'] = aConnection.connectionId(); 363 // args.parameters['reconnecting'] = aConnection.connectionId();
@@ -378,100 +388,113 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
378 }; 388 };
379 389
380 return result; 390 return result;
381 }); 391 });
382 deferredResult.addMethod(this.proxy(), 'handshake'); 392 deferredResult.addMethod(this.proxy(), 'handshake');
383 deferredResult.addCallback(function(someParameters) { 393 deferredResult.addCallback(function(someParameters) {
384 var result; 394 var result;
385 395
386 if (someParameters['M2'] == srpConnection.M2()) { 396 if (someParameters['M2'] == srpConnection.M2()) {
387 result = MochiKit.Async.succeed(someParameters); 397 result = MochiKit.Async.succeed(someParameters);
388 } else { 398 } else {
389 result = MochiKit.Async.fail(Clipperz.PM.Connection.exception.WrongChecksum); 399 result = MochiKit.Async.fail(Clipperz.PM.Connection.exception.WrongChecksum);
390 } 400 }
391 401
392 return result; 402 return result;
393 }); 403 });
394 deferredResult.addCallback(MochiKit.Base.bind(function(someParameters) { 404 deferredResult.addCallback(MochiKit.Base.bind(function(someParameters) {
395 this.setConnectionId(someParameters['connectionId']); 405 this.setConnectionId(someParameters['connectionId']);
396 this.setSharedSecret(srpConnection.K()); 406 this.setSharedSecret(srpConnection.K());
397 407
398 // TODO: ????? 408 // TODO: ?????
399 // if (this.oneTimePassword() != null) { 409 // if (this.oneTimePassword() != null) {
400 /// ?? result = this.user().oneTimePasswordManager().archiveOneTimePassword(this.oneTimePassword())); 410 /// ?? result = this.user().oneTimePasswordManager().archiveOneTimePassword(this.oneTimePassword()));
401 // } 411 // }
412
413 if ((isReconnecting == true) && (this.serverLockValue() != someParameters['lock'])) {
414 throw Clipperz.PM.Connection.exception.StaleData;
415 } else {
416 this.setServerLockValue(someParameters['lock']);
417 }
418
402 return someParameters; 419 return someParameters;
403 }, this)); 420 }, this));
404 // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_loggedIn'); 421 // deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_loggedIn');
405 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); 422 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
406 deferredResult.addCallback(MochiKit.Async.succeed, {result:"done"}); 423 deferredResult.addCallback(MochiKit.Async.succeed, {result:"done"});
407 424
408 deferredResult.callback(); 425 deferredResult.callback();
409 426
410 return deferredResult; 427 return deferredResult;
411 }, 428 },
412 429
413 //========================================================================= 430 //=========================================================================
414 431
415 'logout': function() { 432 'logout': function() {
416 return Clipperz.Async.callbacks("Connection.logout", [ 433 return Clipperz.Async.callbacks("Connection.logout", [
417 MochiKit.Base.method(this, 'setSharedSecret'), 434 MochiKit.Base.method(this, 'setSharedSecret'),
418 MochiKit.Base.method(this.proxy(), 'logout', {}) 435 MochiKit.Base.method(this.proxy(), 'logout', {})
419 ], {trace:false}); 436 ], {trace:false});
420 }, 437 },
421 438
422 //========================================================================= 439 //=========================================================================
423 440
424 'ping': function () { 441 'ping': function () {
425 //TODO: ping the server in order to have a valid session 442 //TODO: ping the server in order to have a valid session
426 }, 443 },
427 444
428 //========================================================================= 445 //=========================================================================
429 446
430 'message': function(aMessageName, someParameters) { 447 'message': function(aMessageName, someParameters) {
431 var args; 448 var args;
449 var parameters;
450
451 parameters = someParameters || {};
452 if (typeof(parameters['user']) != 'undefined') {
453 parameters['user']['lock'] = this.serverLockValue();
454 }
432 455
433//console.log(">>> Connection.message", aMessageName, someParameters); 456//console.log(">>> Connection.message", aMessageName, someParameters);
434 args = { 457 args = {
435 message: aMessageName, 458 message: aMessageName,
436 srpSharedSecret: this.sharedSecret(), 459 srpSharedSecret: this.sharedSecret(),
437 parameters: (someParameters || {}) 460 // parameters: (someParameters || {})
461 parameters: parameters
438 } 462 }
439 463
440 return this.sendMessage(args); 464 return this.sendMessage(args);
441 }, 465 },
442 466
443 //------------------------------------------------------------------------- 467 //-------------------------------------------------------------------------
444 468
445 'sendMessage': function(someArguments) { 469 'sendMessage': function(someArguments) {
446 vardeferredResult; 470 vardeferredResult;
447 471
448 deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false}); 472 deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false});
449 deferredResult.addMethod(this.proxy(), 'message', someArguments); 473 deferredResult.addMethod(this.proxy(), 'message', someArguments);
450 deferredResult.addCallback(MochiKit.Base.bind(function(res) { 474 deferredResult.addCallback(MochiKit.Base.bind(function(res) {
451 if (typeof(res['lock']) != 'undefined') { 475 if (typeof(res['lock']) != 'undefined') {
452 //TODO: ????? 476 this.setServerLockValue(res['lock']);
453 // ?? this.user().setLock(res['lock']);
454 } 477 }
455 return res; 478 return res;
456 }, this)); 479 }, this));
457 480
458 deferredResult.addErrback(MochiKit.Base.method(this, 'messageExceptionHandler'), someArguments); 481 deferredResult.addErrback(MochiKit.Base.method(this, 'messageExceptionHandler'), someArguments);
459 deferredResult.callback(); 482 deferredResult.callback();
460 483
461 return deferredResult 484 return deferredResult
462 }, 485 },
463 486
464 //------------------------------------------------------------------------- 487 //-------------------------------------------------------------------------
465 488
466 'messageExceptionHandler': function(anOriginalMessageArguments, anError) { 489 'messageExceptionHandler': function(anOriginalMessageArguments, anError) {
467 var result; 490 var result;
468 491
469console.log(">>> Connection.messageExceptionHandler", anError, anError.message); 492console.log(">>> Connection.messageExceptionHandler", anError, anError.message);
470 if (anError instanceof MochiKit.Async.CancelledError) { 493 if (anError instanceof MochiKit.Async.CancelledError) {
471 result = anError; 494 result = anError;
472 } else { 495 } else {
473 if ((anError.message == 'Trying to communicate without an active connection')|| 496 if ((anError.message == 'Trying to communicate without an active connection')||
474 (anError.message == 'No tollManager available for current session') 497 (anError.message == 'No tollManager available for current session')
475 ) { 498 ) {
476 result = this.reestablishConnection(anOriginalMessageArguments); 499 result = this.reestablishConnection(anOriginalMessageArguments);
477 } else if (anError.message == 'Session with stale data') { 500 } else if (anError.message == 'Session with stale data') {
@@ -566,48 +589,49 @@ Clipperz.PM.Connection.SRP['1.1'].prototype = MochiKit.Base.update(new Clipperz.
566 589
567 'normalizedCredentials': function(someValues) { 590 'normalizedCredentials': function(someValues) {
568 var result; 591 var result;
569 592
570 result = {} 593 result = {}
571 result['username'] = this.hash()(new Clipperz.ByteArray(someValues['username'] + someValues['password'])).toHexString().substring(2); 594 result['username'] = this.hash()(new Clipperz.ByteArray(someValues['username'] + someValues['password'])).toHexString().substring(2);
572 result['password'] = this.hash()(new Clipperz.ByteArray(someValues['password'] + someValues['username'])).toHexString().substring(2); 595 result['password'] = this.hash()(new Clipperz.ByteArray(someValues['password'] + someValues['username'])).toHexString().substring(2);
573 596
574 return result; 597 return result;
575 }, 598 },
576 599
577 //----------------------------------------------------------------------------- 600 //-----------------------------------------------------------------------------
578 601
579 'hash': function() { 602 'hash': function() {
580 return Clipperz.PM.Crypto.encryptingFunctions.versions['0.2'].hash; 603 return Clipperz.PM.Crypto.encryptingFunctions.versions['0.2'].hash;
581 }, 604 },
582 605
583 //----------------------------------------------------------------------------- 606 //-----------------------------------------------------------------------------
584 __syntaxFix__: "syntax fix" 607 __syntaxFix__: "syntax fix"
585 608
586}); 609});
587 610
588Clipperz.PM.Connection.exception = { 611Clipperz.PM.Connection.exception = {
589 WrongChecksum: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue"), 612 WrongChecksum: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue"),
613 StaleData: new MochiKit.Base.NamedError("Stale data"),
590 UnexpectedRequest:new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.UnexpectedRequest") 614 UnexpectedRequest:new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.UnexpectedRequest")
591}; 615};
592 616
593 617
594Clipperz.PM.Connection.communicationProtocol = { 618Clipperz.PM.Connection.communicationProtocol = {
595 'currentVersion': '0.2', 619 'currentVersion': '0.2',
596 'versions': { 620 'versions': {
597 '0.1': Clipperz.PM.Connection.SRP['1.0'],//Clipperz.Crypto.SRP.versions['1.0'].Connection, 621 '0.1': Clipperz.PM.Connection.SRP['1.0'],//Clipperz.Crypto.SRP.versions['1.0'].Connection,
598 '0.2': Clipperz.PM.Connection.SRP['1.1']//Clipperz.Crypto.SRP.versions['1.1'].Connection 622 '0.2': Clipperz.PM.Connection.SRP['1.1']//Clipperz.Crypto.SRP.versions['1.1'].Connection
599 }, 623 },
600 'fallbackVersions': { 624 'fallbackVersions': {
601 // 'current':'0.1', 625 // 'current':'0.1',
602 '0.2': '0.1', 626 '0.2': '0.1',
603 '0.1': null 627 '0.1': null
604 } 628 }
605}; 629};
606 630
607MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, { 631MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, {
608 'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion] 632 'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
609}); 633});
610 634
611MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.fallbackVersions, { 635MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.fallbackVersions, {
612 'current': Clipperz.PM.Connection.communicationProtocol.fallbackVersions[Clipperz.PM.Connection.communicationProtocol.currentVersion] 636 'current': Clipperz.PM.Connection.communicationProtocol.fallbackVersions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
613}); 637});
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
@@ -23,106 +23,106 @@ refer to http://www.clipperz.com.
23 23
24*/ 24*/
25 25
26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } 26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } 27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
28if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; } 28if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
29 29
30 30
31//############################################################################# 31//#############################################################################
32 32
33Clipperz.PM.DataModel.User = function (args) { 33Clipperz.PM.DataModel.User = function (args) {
34 args = args || {}; 34 args = args || {};
35 35
36 Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments); 36 Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments);
37 37
38 this._username = args.username || null; 38 this._username = args.username || null;
39 this._getPassphraseFunction = args.getPassphraseFunction || null; 39 this._getPassphraseFunction = args.getPassphraseFunction || null;
40 40
41 this._data = null; 41 this._data = null;
42 42
43 this._connection = null; 43 this._connection = null;
44 this._connectionVersion = 'current'; 44 this._connectionVersion = 'current';
45 45
46 this._serverData = null; 46 this._serverData = null;
47 this._serverLockValue = null; 47 //this._serverLockValue = null;
48 this._transientState = null; 48 this._transientState = null;
49 49
50 this._deferredLocks = { 50 this._deferredLocks = {
51 'passphrase': new MochiKit.Async.DeferredLock(), 51 'passphrase': new MochiKit.Async.DeferredLock(),
52 'serverData': new MochiKit.Async.DeferredLock(), 52 'serverData': new MochiKit.Async.DeferredLock(),
53 // 'recordsIndex': new MochiKit.Async.DeferredLock(), 53 // 'recordsIndex': new MochiKit.Async.DeferredLock(),
54 // 'directLoginsIndex':new MochiKit.Async.DeferredLock() 54 // 'directLoginsIndex':new MochiKit.Async.DeferredLock()
55 // 'preferences': new MochiKit.Async.DeferredLock() 55 // 'preferences': new MochiKit.Async.DeferredLock()
56 // 'oneTimePasswords': new MochiKit.Async.DeferredLock() 56 // 'oneTimePasswords': new MochiKit.Async.DeferredLock()
57 '__syntaxFix__': 'syntax fix' 57 '__syntaxFix__': 'syntax fix'
58 }; 58 };
59 59
60 return this; 60 return this;
61} 61}
62 62
63Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { 63Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
64 64
65 'toString': function () { 65 'toString': function () {
66 return "Clipperz.PM.DataModel.User - " + this.username(); 66 return "Clipperz.PM.DataModel.User - " + this.username();
67 }, 67 },
68 68
69 //------------------------------------------------------------------------- 69 //-------------------------------------------------------------------------
70 70
71 'username': function () { 71 'username': function () {
72 return this._username; 72 return this._username;
73 }, 73 },
74 74
75 'setUsername': function (aValue) { 75 'setUsername': function (aValue) {
76 this._username = aValue; 76 this._username = aValue;
77 }, 77 },
78 78
79 //------------------------------------------------------------------------- 79 //-------------------------------------------------------------------------
80 80
81 'displayName': function() { 81 'displayName': function() {
82 return "" + this.username() + ""; 82 return "" + this.username() + "";
83 }, 83 },
84 84
85 //------------------------------------------------------------------------- 85 //-------------------------------------------------------------------------
86 86
87 'data': function () { 87 'data': function () {
88 if (this._data == null) { 88 if (this._data == null) {
89 this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/); 89 this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/);
90 }; 90 };
91 91
92 return this._data; 92 return this._data;
93 }, 93 },
94 94
95 //------------------------------------------------------------------------- 95 //-------------------------------------------------------------------------
96 96/*
97 'serverLockValue': function () { 97 'serverLockValue': function () {
98 return this._serverLockValue; 98 return this._serverLockValue;
99 }, 99 },
100 100
101 'setServerLockValue': function (aValue) { 101 'setServerLockValue': function (aValue) {
102 this._serverLockValue = aValue; 102 this._serverLockValue = aValue;
103 }, 103 },
104 104*/
105 //------------------------------------------------------------------------- 105 //-------------------------------------------------------------------------
106 106
107 'transientState': function () { 107 'transientState': function () {
108 if (this._transientState == null) { 108 if (this._transientState == null) {
109 this._transientState = {} 109 this._transientState = {}
110 } 110 }
111 111
112 return this._transientState; 112 return this._transientState;
113 }, 113 },
114 114
115 'resetTransientState': function (isCommitting) { 115 'resetTransientState': function (isCommitting) {
116 this._transientState = null; 116 this._transientState = null;
117 }, 117 },
118 118
119 //------------------------------------------------------------------------- 119 //-------------------------------------------------------------------------
120 120
121 'deferredLockForSection': function(aSectionName) { 121 'deferredLockForSection': function(aSectionName) {
122 return this._deferredLocks[aSectionName]; 122 return this._deferredLocks[aSectionName];
123 }, 123 },
124 124
125 //------------------------------------------------------------------------- 125 //-------------------------------------------------------------------------
126 126
127 'getPassphrase': function() { 127 'getPassphrase': function() {
128 var deferredResult; 128 var deferredResult;
@@ -199,76 +199,76 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
199 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase') 199 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
200 }), 200 }),
201 'oneTimePasswords': new Clipperz.PM.DataModel.User.Header.OneTimePasswords({ 201 'oneTimePasswords': new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
202 'name':'preferences', 202 'name':'preferences',
203 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase') 203 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
204 }) 204 })
205 } 205 }
206 }; 206 };
207 207
208 // this._serverLockValue = Clipperz.PM.Crypto.randomKey(); 208 // this._serverLockValue = Clipperz.PM.Crypto.randomKey();
209 }, 209 },
210 210
211 //......................................................................... 211 //.........................................................................
212 212
213 'registerAsNewAccount': function () { 213 'registerAsNewAccount': function () {
214 var deferredResult; 214 var deferredResult;
215 215
216 deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false}); 216 deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false});
217 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3}); 217 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
218 deferredResult.addMethod(this, 'initialSetupWithNoData') 218 deferredResult.addMethod(this, 'initialSetupWithNoData')
219 deferredResult.addMethod(this, 'getPassphrase'); 219 deferredResult.addMethod(this, 'getPassphrase');
220 deferredResult.addMethod(this, 'prepareRemoteDataWithKey'); 220 deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
221 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); 221 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
222 deferredResult.addMethod(this.connection(), 'register'); 222 deferredResult.addMethod(this.connection(), 'register');
223 deferredResult.addCallback(MochiKit.Base.itemgetter('lock')); 223 // deferredResult.addCallback(MochiKit.Base.itemgetter('lock'));
224 deferredResult.addMethod(this, 'setServerLockValue'); 224 // deferredResult.addMethod(this, 'setServerLockValue');
225 deferredResult.addCallbackPass(MochiKit.Signal.signal,Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered'); 225 deferredResult.addCallbackPass(MochiKit.Signal.signal,Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered');
226 226
227 // deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure')); 227 // deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure'));
228 228
229 deferredResult.callback(); 229 deferredResult.callback();
230 230
231 return deferredResult; 231 return deferredResult;
232 }, 232 },
233 233
234 //------------------------------------------------------------------------- 234 //-------------------------------------------------------------------------
235 235
236 'login': function () { 236 'login': function () {
237 var deferredResult; 237 var deferredResult;
238 238
239 deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false}); 239 deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false});
240 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3}); 240 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
241 deferredResult.addMethod(this, 'getPassphrase'); 241 deferredResult.addMethod(this, 'getPassphrase');
242 deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue); 242 deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue);
243 deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [ 243 deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [
244 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}), 244 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}),
245 MochiKit.Base.method(this, 'getCredentials'), 245 MochiKit.Base.method(this, 'getCredentials'),
246 MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'), 246 MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'),
247 MochiKit.Base.method(this.data(), 'setValue', 'passphrase') 247 MochiKit.Base.method(this.data(), 'setValue', 'passphrase')
248 ], [])); 248 ], []));
249 deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase')); 249 deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase'));
250 deferredResult.addMethod(this.connection(), 'login'); 250 deferredResult.addMethod(this.connection(), 'login', false);
251 deferredResult.addCallbackPass(MochiKit.Signal.signal,Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn'); 251 deferredResult.addCallbackPass(MochiKit.Signal.signal,Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn');
252 deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback')); 252 deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
253 253
254 deferredResult.callback(); 254 deferredResult.callback();
255 255
256 return deferredResult; 256 return deferredResult;
257 }, 257 },
258 258
259 //......................................................................... 259 //.........................................................................
260 260
261 'handleConnectionFallback': function(aValue) { 261 'handleConnectionFallback': function(aValue) {
262 var result; 262 var result;
263 263
264 if (aValue instanceof MochiKit.Async.CancelledError) { 264 if (aValue instanceof MochiKit.Async.CancelledError) {
265 result = aValue; 265 result = aValue;
266 } else { 266 } else {
267 this.setConnectionVersion(Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()]); 267 this.setConnectionVersion(Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()]);
268 268
269 if (this.connectionVersion() != null) { 269 if (this.connectionVersion() != null) {
270 result = new Clipperz.Async.Deferred("User.handleConnectionFallback - retry"); 270 result = new Clipperz.Async.Deferred("User.handleConnectionFallback - retry");
271 271
272 result.addMethod(this, 'login'); 272 result.addMethod(this, 'login');
273 result.callback(); 273 result.callback();
274 } else { 274 } else {
@@ -308,49 +308,49 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
308 308
309 if (anHeader.charAt(0) == '{') { 309 if (anHeader.charAt(0) == '{') {
310 varheaderData; 310 varheaderData;
311 311
312 headerData = Clipperz.Base.evalJSON(anHeader); 312 headerData = Clipperz.Base.evalJSON(anHeader);
313 result = headerData['version']; 313 result = headerData['version'];
314 } else { 314 } else {
315 result = 'LEGACY'; 315 result = 'LEGACY';
316 } 316 }
317 317
318 return result; 318 return result;
319 }, 319 },
320 320
321 //------------------------------------------------------------------------- 321 //-------------------------------------------------------------------------
322 322
323 'unpackServerData': function (someServerData) { 323 'unpackServerData': function (someServerData) {
324 var unpackedData; 324 var unpackedData;
325 var headerVersion; 325 var headerVersion;
326 326
327 varrecordsIndex; 327 varrecordsIndex;
328 var preferences; 328 var preferences;
329 var oneTimePasswords; 329 var oneTimePasswords;
330 330
331//console.log(">>> ***************** user.unpackServerData", someServerData); 331//console.log(">>> ***************** user.unpackServerData", someServerData);
332 this.setServerLockValue(someServerData['lock']); 332 // this.setServerLockValue(someServerData['lock']);
333 333
334 headerVersion = this.headerFormatVersion(someServerData['header']); 334 headerVersion = this.headerFormatVersion(someServerData['header']);
335 335
336 switch (headerVersion) { 336 switch (headerVersion) {
337 case 'LEGACY': 337 case 'LEGACY':
338 varlegacyHeader; 338 varlegacyHeader;
339 339
340 legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({ 340 legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({
341 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'), 341 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
342 'remoteData': { 342 'remoteData': {
343 'data': someServerData['header'], 343 'data': someServerData['header'],
344 'version': someServerData['version'], 344 'version': someServerData['version'],
345 'recordsStats': someServerData['recordsStats'] 345 'recordsStats': someServerData['recordsStats']
346 }, 346 },
347 // 'encryptedDataKeypath': 'data', 347 // 'encryptedDataKeypath': 'data',
348 // 'encryptedVersionKeypath': 'version', 348 // 'encryptedVersionKeypath': 'version',
349 'retrieveRecordDetailFunction':MochiKit.Base.method(this, 'getRecordDetail') 349 'retrieveRecordDetailFunction':MochiKit.Base.method(this, 'getRecordDetail')
350 }); 350 });
351 351
352 recordsIndex = legacyHeader; 352 recordsIndex = legacyHeader;
353 preferences = legacyHeader; 353 preferences = legacyHeader;
354 oneTimePasswords= legacyHeader; 354 oneTimePasswords= legacyHeader;
355 break; 355 break;
356 case '0.1': 356 case '0.1':
@@ -641,50 +641,50 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
641 deferredResult.collectResults({ 641 deferredResult.collectResults({
642 'header': [ 642 'header': [
643 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasPendingChanges'), 643 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasPendingChanges'),
644 MochiKit.Base.values 644 MochiKit.Base.values
645 ], 645 ],
646 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasPendingChanges') 646 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasPendingChanges')
647 }); 647 });
648 deferredResult.addCallback(Clipperz.Async.or); 648 deferredResult.addCallback(Clipperz.Async.or);
649 deferredResult.callback(); 649 deferredResult.callback();
650 // recordsIndex = legacyHeader; 650 // recordsIndex = legacyHeader;
651 // preferences = legacyHeader; 651 // preferences = legacyHeader;
652 // oneTimePasswords= legacyHeader; 652 // oneTimePasswords= legacyHeader;
653 653
654 return deferredResult; 654 return deferredResult;
655 }, 655 },
656 656
657 //========================================================================= 657 //=========================================================================
658 658
659 'commitTransientState': function () { 659 'commitTransientState': function () {
660 return Clipperz.Async.callbacks("User.commitTransientState", [ 660 return Clipperz.Async.callbacks("User.commitTransientState", [
661 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'), 661 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'),
662 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'), 662 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'),
663 663
664 MochiKit.Base.method(this, 'transientState'), 664 MochiKit.Base.method(this, 'transientState'),
665 MochiKit.Base.itemgetter('lock'), 665 // MochiKit.Base.itemgetter('lock'),
666 MochiKit.Base.method(this, 'setServerLockValue'), 666 // MochiKit.Base.method(this, 'setServerLockValue'),
667 MochiKit.Base.method(this, 'resetTransientState', true) 667 MochiKit.Base.method(this, 'resetTransientState', true)
668 ], {trace:false}); 668 ], {trace:false});
669 }, 669 },
670 670
671 //------------------------------------------------------------------------- 671 //-------------------------------------------------------------------------
672 672
673 'revertChanges': function () { 673 'revertChanges': function () {
674 return Clipperz.Async.callbacks("User.revertChanges", [ 674 return Clipperz.Async.callbacks("User.revertChanges", [
675 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'), 675 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'),
676 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'), 676 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'),
677 MochiKit.Base.method(this, 'resetTransientState', false) 677 MochiKit.Base.method(this, 'resetTransientState', false)
678 ], {trace:false}); 678 ], {trace:false});
679 }, 679 },
680 680
681 //========================================================================= 681 //=========================================================================
682 682
683 'deleteAllCleanTextData': function () { 683 'deleteAllCleanTextData': function () {
684 return Clipperz.Async.callbacks("User.deleteAllCleanTextData", [ 684 return Clipperz.Async.callbacks("User.deleteAllCleanTextData", [
685 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'deleteAllCleanTextData'), 685 MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'deleteAllCleanTextData'),
686 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'deleteAllCleanTextData'), 686 MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'deleteAllCleanTextData'),
687 687
688 MochiKit.Base.method(this.data(), 'removeAllData'), 688 MochiKit.Base.method(this.data(), 'removeAllData'),
689 MochiKit.Base.method(this, 'resetTransientState', false) 689 MochiKit.Base.method(this, 'resetTransientState', false)
690 ], {trace:false}); 690 ], {trace:false});
@@ -719,49 +719,49 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
719 719
720 'prepareRemoteDataWithKey': function (aKey /*, aCurrentKey*/) { 720 'prepareRemoteDataWithKey': function (aKey /*, aCurrentKey*/) {
721 var deferredResult; 721 var deferredResult;
722 varresult; 722 varresult;
723 723
724 result = {}; 724 result = {};
725 deferredResult = new Clipperz.Async.Deferred("User.prepareRemoteDataWithKey", {trace:false}); 725 deferredResult = new Clipperz.Async.Deferred("User.prepareRemoteDataWithKey", {trace:false});
726 deferredResult.addMethod(this, 'invokeMethodNamedOnHeader', 'prepareRemoteDataWithKey', aKey /*, aCurrentKey*/); 726 deferredResult.addMethod(this, 'invokeMethodNamedOnHeader', 'prepareRemoteDataWithKey', aKey /*, aCurrentKey*/);
727 deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) { 727 deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) {
728 var header; 728 var header;
729 729
730 header = {}; 730 header = {};
731 header['records'] = someHeaderPackedData['recordIndex']['records']; 731 header['records'] = someHeaderPackedData['recordIndex']['records'];
732 header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins']; 732 header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
733 header['preferences'] = {'data': someHeaderPackedData['preferences']['data']}; // this._serverData['header']['preferences']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['preferences']; //??????????? 733 header['preferences'] = {'data': someHeaderPackedData['preferences']['data']}; // this._serverData['header']['preferences']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['preferences']; //???????????
734 header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']}; // this._serverData['header']['oneTimePasswords']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['oneTimePasswords']; //??????????? 734 header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']}; // this._serverData['header']['oneTimePasswords']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['oneTimePasswords']; //???????????
735 header['version'] = '0.1'; 735 header['version'] = '0.1';
736 736
737 aResult['header'] = Clipperz.Base.serializeJSON(header); 737 aResult['header'] = Clipperz.Base.serializeJSON(header);
738 aResult['statistics'] = this._serverData['statistics']; //"someHeaderPackedData['statistics']['data']"; 738 aResult['statistics'] = this._serverData['statistics']; //"someHeaderPackedData['statistics']['data']";
739 739
740 return aResult; 740 return aResult;
741 }, this), result); 741 }, this), result);
742 deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion); 742 deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
743 deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue()); 743 // deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue());
744 deferredResult.callback(); 744 deferredResult.callback();
745 745
746 return deferredResult; 746 return deferredResult;
747 }, 747 },
748 748
749 //========================================================================= 749 //=========================================================================
750 750
751 'saveChanges': function () { 751 'saveChanges': function () {
752 vardeferredResult; 752 vardeferredResult;
753 var messageParameters; 753 var messageParameters;
754 754
755 messageParameters = {}; 755 messageParameters = {};
756 756
757 deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false}); 757 deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false});
758 758
759 deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex'); 759 deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex');
760 deferredResult.addCallback(MochiKit.Base.methodcaller('prepareRemoteDataForChangedRecords')); 760 deferredResult.addCallback(MochiKit.Base.methodcaller('prepareRemoteDataForChangedRecords'));
761 deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'records'); 761 deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'records');
762 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); 762 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
763 763
764 deferredResult.addMethod(this, 'getPassphrase'); 764 deferredResult.addMethod(this, 'getPassphrase');
765 deferredResult.addMethod(this, 'prepareRemoteDataWithKey'); 765 deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
766 deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'user'); 766 deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'user');
767 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress'); 767 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
28if (typeof(Clipperz.PM.PIN) == 'undefined') { Clipperz.PM.PIN = {}; }
29
30MochiKit.Base.update(Clipperz.PM.PIN, {
31
32 //-------------------------------------------------------------------------
33
34 '__repr__': function () {
35 return "[" + this.NAME + " " + this.VERSION + "]";
36 },
37
38 //-------------------------------------------------------------------------
39
40 'toString': function () {
41 return this.__repr__();
42 },
43
44 'CREDENTIALS': 'CLIPPERZ.CREDENTIALS',
45 'FAILURE_COUNT': 'CLIPPERZ.FAILED_LOGIN_COUNT',
46 'ALLOWED_RETRY': 3,
47
48 //-------------------------------------------------------------------------
49
50 'isSet': function () {
51 return (this.storedCredentials() != null);
52 },
53
54 'storedCredentials': function () {
55 return localStorage[this.CREDENTIALS];
56 },
57
58 //-------------------------------------------------------------------------
59
60 'recordFailedAttempt': function () {
61 varfailureCount;
62 varresult;
63
64 failureCount = localStorage[this.FAILURE_COUNT];
65
66 if (failureCount == null) {
67 failureCount = 0
68 }
69
70 failureCount ++;
71
72 if (failureCount < this.ALLOWED_RETRY) {
73 localStorage[this.FAILURE_COUNT] = failureCount;
74 result = failureCount;
75 } else {
76 this.removeLocalCredentials();
77 result = -1;
78 }
79
80 return result;
81 },
82
83 'resetFailedAttemptCount': function () {
84 localStorage.removeItem(this.FAILURE_COUNT);
85 },
86
87 'failureCount': function () {
88 return localStorage[this.FAILURE_COUNT];
89 },
90
91 //-------------------------------------------------------------------------
92
93 'deriveKeyFromPin': function (aPIN) {
94 return Clipperz.Crypto.SHA.sha256(new Clipperz.ByteArray(aPIN));
95 },
96
97 'credentialsWithPIN': function (aPIN) {
98 varbyteArrayValue;
99 var decryptedValue;
100 varresult;
101
102 byteArrayValue = (new Clipperz.ByteArray()).appendBase64String(localStorage[this.CREDENTIALS]);
103 decryptedValue = Clipperz.Crypto.AES.decrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).asString();
104 try {
105 result = Clipperz.Base.evalJSON(decryptedValue);
106 } catch (error) {
107 result = {'username':'fakeusername', 'passphrase':'fakepassphrase'};
108 }
109
110 return result;
111 },
112
113 'setCredentialsWithPIN': function (aPIN, someCredentials) {
114 varencodedValue;
115 varbyteArrayValue;
116 var encryptedValue;
117
118 encodedValue = Clipperz.Base.serializeJSON(someCredentials);
119 byteArrayValue = new Clipperz.ByteArray(encodedValue);
120 encryptedValue = Clipperz.Crypto.AES.encrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).toBase64String();
121
122 localStorage[this.CREDENTIALS] = encryptedValue;
123 },
124
125 'removeLocalCredentials': function () {
126 localStorage.removeItem(this.CREDENTIALS);
127 localStorage.removeItem(this.FAILURE_COUNT);
128 },
129
130 //-------------------------------------------------------------------------
131 __syntaxFix__: "syntax fix"
132
133});
134
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
27if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
28
29//=============================================================================
30
31Clipperz.PM.Proxy.OfflineCache = function(args) {
32 args = args || {};
33
34 Clipperz.PM.Proxy.Offline.superclass.constructor.call(this, args);
35
36 //this._dataStore = args.dataStore || new Clipperz.PM.Proxy.Offline.DataStore(args);
37
38 return this;
39}
40
41Clipperz.Base.extend(Clipperz.PM.Proxy.OfflineCache, Clipperz.PM.Proxy, {
42
43 'toString': function () {
44 return "Clipperz.PM.Proxy.OfflineCache";
45 },
46
47 //-------------------------------------------------------------------------
48
49 //'dataStore': function () {
50 // return this._dataStore;
51 //},
52
53 //-------------------------------------------------------------------------
54
55 'sendMessage': function(aFunctionName, someParameters) {
56 throw Clipperz.Base.exception.MethodNotImplementedYet;
57 // return this.dataStore().processMessage(aFunctionName, someParameters);
58 },
59
60 //-------------------------------------------------------------------------
61
62 __syntaxFix__: "syntax fix"
63
64});
65
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
@@ -385,48 +385,52 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.BaseComponent, /*Ext.Compo
385 }, 385 },
386 386
387 //------------------------------------------------------------------------- 387 //-------------------------------------------------------------------------
388 388
389 'getId': function(aValue) { 389 'getId': function(aValue) {
390 varresult; 390 varresult;
391 391
392 if (typeof(aValue) != 'undefined') { 392 if (typeof(aValue) != 'undefined') {
393 result = this._ids[aValue]; 393 result = this._ids[aValue];
394 394
395 if (typeof(result) == 'undefined') { 395 if (typeof(result) == 'undefined') {
396 _Clipperz_PM_Components_base_id_ ++; 396 _Clipperz_PM_Components_base_id_ ++;
397 397
398 result = "Clipperz_PM_Components_" + aValue + "_" + _Clipperz_PM_Components_base_id_; 398 result = "Clipperz_PM_Components_" + aValue + "_" + _Clipperz_PM_Components_base_id_;
399 this._ids[aValue] = result; 399 this._ids[aValue] = result;
400 } 400 }
401 } else { 401 } else {
402 // result = Clipperz.PM.UI.Common.Components.BaseComponent.superclass.getId.call(this); 402 // result = Clipperz.PM.UI.Common.Components.BaseComponent.superclass.getId.call(this);
403 throw "call to BaseComponent.getId with an undefined value"; 403 throw "call to BaseComponent.getId with an undefined value";
404 } 404 }
405 405
406 return result; 406 return result;
407 }, 407 },
408 408
409 'getAnchor': function (aValue) {
410 return '#' + this.getId(aValue);
411 },
412
409 //------------------------------------------------------------------------- 413 //-------------------------------------------------------------------------
410 414
411 'getElement': function(aValue) { 415 'getElement': function(aValue) {
412 return Clipperz.DOM.get(this.getId(aValue)); 416 return Clipperz.DOM.get(this.getId(aValue));
413 }, 417 },
414 418
415 //------------------------------------------------------------------------- 419 //-------------------------------------------------------------------------
416 420
417 'hideElement': function(anElementName) { 421 'hideElement': function(anElementName) {
418 MochiKit.Style.hideElement(this.getElement(anElementName)); 422 MochiKit.Style.hideElement(this.getElement(anElementName));
419 }, 423 },
420 424
421 'showElement': function(anElementName) { 425 'showElement': function(anElementName) {
422 MochiKit.Style.showElement(this.getElement(anElementName)); 426 MochiKit.Style.showElement(this.getElement(anElementName));
423 }, 427 },
424 428
425 //------------------------------------------------------------------------- 429 //-------------------------------------------------------------------------
426 430
427 'activate': function () { 431 'activate': function () {
428 this._isActive = true; 432 this._isActive = true;
429 }, 433 },
430 434
431 'deactivate': function () { 435 'deactivate': function () {
432 this._isActive = false; 436 this._isActive = false;
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
@@ -41,65 +41,73 @@ Clipperz.PM.UI.Common.Components.Button = function(args) {
41 41
42//============================================================================= 42//=============================================================================
43 43
44Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Common.Components.BaseComponent, { 44Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Common.Components.BaseComponent, {
45 45
46 //------------------------------------------------------------------------- 46 //-------------------------------------------------------------------------
47 47
48 'toString': function () { 48 'toString': function () {
49 return "Clipperz.PM.UI.Common.Components.Button component"; 49 return "Clipperz.PM.UI.Common.Components.Button component";
50 }, 50 },
51 51
52 //------------------------------------------------------------------------- 52 //-------------------------------------------------------------------------
53 53
54 'text': function () { 54 'text': function () {
55 return this._text; 55 return this._text;
56 }, 56 },
57 57
58 'isDefault': function () { 58 'isDefault': function () {
59 return this._isDefault; 59 return this._isDefault;
60 }, 60 },
61 61
62 //------------------------------------------------------------------------- 62 //-------------------------------------------------------------------------
63 63
64 'renderSelf': function () { 64 'renderSelf': function () {
65 this.append(this.element(), {tag:'div', id:this.getId('wrapper'), cls:'button_wrapper', children:[ 65/*
66 this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button_wrapper', children:[
66 {tag:'div', id:this.getId('bodyWrapper'), cls:'button_bodyWrapper', children:[ 67 {tag:'div', id:this.getId('bodyWrapper'), cls:'button_bodyWrapper', children:[
67 {tag:'div', id:this.getId('body'), cls:'button_body', children:[ 68 {tag:'div', id:this.getId('body'), cls:'button_body', children:[
68 {tag:'span', html:this.text()} 69 {tag:'span', html:this.text()}
69 ]}, 70 ]},
70 {tag:'div', id:this.getId('footer'), cls:'button_footer'} 71 {tag:'div', id:this.getId('footer'), cls:'button_footer'}
71 ]} 72 ]}
72 ]}); 73 ]});
74*/
75/*
76 this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button', children:[
77 {tag:'span', html:this.text()}
78 ]});
79*/
80 this.append(this.element(), {tag:'a', id:this.getId('button'), cls:'button', html:this.text()});
73 81
74 if (this.isDefault()) { 82 if (this.isDefault()) {
75 MochiKit.DOM.addElementClass(this.getId('wrapper'), 'default'); 83 MochiKit.DOM.addElementClass(this.getId('button'), 'default');
76 } 84 }
77 85
78 MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseenter',this, 'handleOnMouseEnter'); 86 // MochiKit.Signal.connect(this.getId('button'), 'onmouseenter',this, 'handleOnMouseEnter');
79 MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseleave',this, 'handleOnMouseLeave'); 87 // MochiKit.Signal.connect(this.getId('button'), 'onmouseleave',this, 'handleOnMouseLeave');
80 MochiKit.Signal.connect(this.getId('wrapper'), 'onmousedown',this, 'handleOnMouseDown'); 88 // MochiKit.Signal.connect(this.getId('button'), 'onmousedown',this, 'handleOnMouseDown');
81 MochiKit.Signal.connect(this.getId('wrapper'), 'onclick', this, 'handleOnClick'); 89 MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'handleOnClick');
82 }, 90 },
83 91
84 //------------------------------------------------------------------------- 92 //-------------------------------------------------------------------------
85 93/*
86 'handleOnMouseEnter': function (anEvent) { 94 'handleOnMouseEnter': function (anEvent) {
87 MochiKit.DOM.addElementClass(this.getId('wrapper'), 'hover'); 95 MochiKit.DOM.addElementClass(this.getId('button'), 'hover');
88 }, 96 },
89 97
90 'handleOnMouseLeave': function (anEvent) { 98 'handleOnMouseLeave': function (anEvent) {
91 MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'hover'); 99 MochiKit.DOM.removeElementClass(this.getId('button'), 'hover');
92 MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'clicked'); 100 MochiKit.DOM.removeElementClass(this.getId('button'), 'clicked');
93 }, 101 },
94 102
95 'handleOnMouseDown': function (anEvent) { 103 'handleOnMouseDown': function (anEvent) {
96 MochiKit.DOM.addElementClass(this.getId('wrapper'), 'clicked'); 104 MochiKit.DOM.addElementClass(this.getId('button'), 'clicked');
97 }, 105 },
98 106 */
99 'handleOnClick': function (anEvent) { 107 'handleOnClick': function (anEvent) {
100 MochiKit.Signal.signal(this, 'onclick', anEvent); 108 MochiKit.Signal.signal(this, 'onclick', anEvent);
101 }, 109 },
102 110
103 //------------------------------------------------------------------------- 111 //-------------------------------------------------------------------------
104 __syntaxFix__: "syntax fix" 112 __syntaxFix__: "syntax fix"
105}); 113});
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
@@ -77,93 +77,111 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.SimpleMessagePanel, Clippe
77 } 77 }
78 }, 78 },
79 79
80 //------------------------------------------------------------------------- 80 //-------------------------------------------------------------------------
81 81
82 'text': function () { 82 'text': function () {
83 return this._text; 83 return this._text;
84 }, 84 },
85 85
86 'setText': function (aValue) { 86 'setText': function (aValue) {
87 this._text = aValue; 87 this._text = aValue;
88 88
89 if (this.getElement('text') != null) { 89 if (this.getElement('text') != null) {
90 this.getElement('text').innerHTML = aValue; 90 this.getElement('text').innerHTML = aValue;
91 } 91 }
92 }, 92 },
93 93
94 //------------------------------------------------------------------------- 94 //-------------------------------------------------------------------------
95 95
96 'type': function () { 96 'type': function () {
97 return this._type; 97 return this._type;
98 }, 98 },
99 99
100 'setType': function (aValue) { 100 'setType': function (aValue) {
101 if (this.getElement('icon') != null) { 101 // if (this.getElement('icon') != null) {
102 MochiKit.DOM.removeElementClass(this.getId('icon'), this._type); 102 // MochiKit.DOM.removeElementClass(this.getId('icon'), this._type);
103 MochiKit.DOM.addElementClass(this.getId('icon'), aValue); 103 // MochiKit.DOM.addElementClass(this.getId('icon'), aValue);
104 } 104 // }
105 105
106 this._type = aValue; 106 this._type = aValue;
107 }, 107 },
108 108
109 'icon': function () {
110 var type = this.type();
111 var result;
112
113 if (type == 'ALERT') {
114 result = '!';
115 } else if (type == 'INFO') {
116 result = 'i';
117 } else if (type == 'ERROR') {
118 result = '!';
119 }
120
121 return result;
122 },
123
109 //------------------------------------------------------------------------- 124 //-------------------------------------------------------------------------
110 125
111 'buttons': function () { 126 'buttons': function () {
112 return this._buttons; 127 return this._buttons;
113 }, 128 },
114 129
115 'setButtons': function (someValues) { 130 'setButtons': function (someValues) {
116 MochiKit.Iter.forEach(this.buttonComponents(), MochiKit.Base.methodcaller('clear')); 131 MochiKit.Iter.forEach(this.buttonComponents(), MochiKit.Base.methodcaller('clear'));
117 132
118 this._buttons = someValues; 133 this._buttons = someValues;
119 134
120 if (this.getElement('buttonArea') != null) { 135 if (this.getElement('buttonArea') != null) {
121 this.renderButtons(); 136 this.renderButtons();
122 } 137 }
123 }, 138 },
124 139
125 //......................................................................... 140 //.........................................................................
126 141
127 'buttonComponents': function () { 142 'buttonComponents': function () {
128 return this._buttonComponents; 143 return this._buttonComponents;
129 }, 144 },
130 145
131 //------------------------------------------------------------------------- 146 //-------------------------------------------------------------------------
132 147
133 'renderSelf': function() { 148 'renderSelf': function() {
134 this.append(this.element(), {tag:'div', cls:'SimpleMessagePanel', id:this.getId('panel'), children: [ 149 this.append(this.element(), {tag:'div', cls:'SimpleMessagePanel', id:this.getId('panel'), children: [
135 {tag:'div', cls:'header', children:[]}, 150 // {tag:'div', cls:'header', children:[]},
136 {tag:'div', cls:'body', children:[ 151 {tag:'div', cls:'body', children:[
137 {tag:'div', id:this.getId('icon'),cls:'img ' + this.type(), children:[{tag:'div'}]}, 152 // {tag:'div', id:this.getId('icon'),cls:'img ' + this.type(), children:[{tag:'div'}]},
153 {tag:'div', /*id:this.getId('icon'),*/cls:'img ' + this.type(), children:[{tag:'canvas', id:this.getId('icon')}]},
138 {tag:'h3', id:this.getId('title'),html:this.title()}, 154 {tag:'h3', id:this.getId('title'),html:this.title()},
139 {tag:'p', id:this.getId('text'),html:this.text()}, 155 {tag:'p', id:this.getId('text'),html:this.text()},
140 {tag:'div', id:this.getId('container')}, 156 {tag:'div', id:this.getId('container')},
141 {tag:'div', id:this.getId('buttonArea'), cls:'buttonArea', children:[]} 157 {tag:'div', id:this.getId('buttonArea'), cls:'buttonArea', children:[]}
142 ]}, 158 ]}
143 {tag:'div', cls:'footer', children:[]} 159 // {tag:'div', cls:'footer', children:[]}
144 ]}); 160 ]});
145 161
162 Clipperz.PM.UI.Canvas.marks[this.icon()](this.getElement('icon'), "#ffffff");
163
146 MochiKit.Signal.connect(this.getId('panel'), 'onkeydown', this, 'keyDownHandler'); 164 MochiKit.Signal.connect(this.getId('panel'), 'onkeydown', this, 'keyDownHandler');
147 165
148 this.renderButtons(); 166 this.renderButtons();
149 }, 167 },
150 168
151 //------------------------------------------------------------------------- 169 //-------------------------------------------------------------------------
152 170
153 'renderButtons': function () { 171 'renderButtons': function () {
154 this.getElement('buttonArea').innerHTML = ''; 172 this.getElement('buttonArea').innerHTML = '';
155 173
156 MochiKit.Base.map(MochiKit.Base.bind(function (aButton) { 174 MochiKit.Base.map(MochiKit.Base.bind(function (aButton) {
157 var buttonElement; 175 var buttonElement;
158 var buttonComponent; 176 var buttonComponent;
159 177
160 // element = this.append(this.getElement('buttonArea'), {tag:'div', cls:'button' + (aButton['isDefault'] === true ? ' default' : ''), children:[ 178 // element = this.append(this.getElement('buttonArea'), {tag:'div', cls:'button' + (aButton['isDefault'] === true ? ' default' : ''), children:[
161 // {tag:'a', href:'#'/*, id:this.getId('buttonLink')*/, html:aButton['text']} 179 // {tag:'a', href:'#'/*, id:this.getId('buttonLink')*/, html:aButton['text']}
162 // ]}); 180 // ]});
163 181
164 buttonElement = this.append(this.getElement('buttonArea'), {tag:'div'}); 182 buttonElement = this.append(this.getElement('buttonArea'), {tag:'div'});
165 buttonComponent = new Clipperz.PM.UI.Common.Components.Button({'element':buttonElement, 'text':aButton['text'], 'isDefault':aButton['isDefault']}); 183 buttonComponent = new Clipperz.PM.UI.Common.Components.Button({'element':buttonElement, 'text':aButton['text'], 'isDefault':aButton['isDefault']});
166 this.buttonComponents().push(buttonComponent); 184 this.buttonComponents().push(buttonComponent);
167 185
168 MochiKit.Signal.connect(buttonComponent, 'onclick', MochiKit.Base.method(this, 'buttonEventHandler', aButton)); 186 MochiKit.Signal.connect(buttonComponent, 'onclick', MochiKit.Base.method(this, 'buttonEventHandler', aButton));
169 }, this), MochiKit.Iter.reversed(this.buttons())); 187 }, this), MochiKit.Iter.reversed(this.buttons()));
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
27
28Clipperz.PM.UI.Mobile.Components.CardDetail = function(args) {
29 args = args || {};
30
31 Clipperz.PM.UI.Mobile.Components.CardDetail.superclass.constructor.apply(this, arguments);
32
33 //this._cardReference = null;
34
35 return this;
36}
37
38//=============================================================================
39
40Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, {
41
42 //-------------------------------------------------------------------------
43
44 'toString': function () {
45 return "Clipperz.PM.UI.Mobile.Components.CardDetail component";
46 },
47
48 //-------------------------------------------------------------------------
49/*
50 'cardReference': function () {
51 return this._cardReference;
52 },
53
54 'setCardReference': function (aValue) {
55 this._cardReference = aValue;
56 },
57*/
58 //-------------------------------------------------------------------------
59
60 'renderSelf': function () {
61console.log("CardDetail.renderSelf");
62 this.append(this.element(), {tag:'div', cls:'cardDetail', children:[
63 {tag:'div', cls:'toolbar', children:[
64 {tag:'a', href:'#', cls:'back', html:"List"},
65 {tag:'h1', id:this.getId('cardTitle'), html:"…"}
66 ]},
67 {tag:'div', cls:'scroll', id:this.getId('cardDetails'), children:[
68 ]}
69 ]});
70 },
71/*
72 'renderSelf': function() {
73 this.append(this.element(), [
74 {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[
75 {tag:'div', id:this.getId('progressBar')} //,
76 ]}
77 ]);
78
79 this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
80 MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0);
81 },
82*/
83
84 'setTitle': function (aValue) {
85 this.getElement('cardTitle').innerHTML = aValue;
86 },
87
88 'fieldListElement': function () {
89 varresult;
90
91 result = this.getElement('fieldList');
92 if (result == null) {
93 result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('fieldList')});
94 }
95
96 return result;
97 },
98
99 'renderFieldValues': function (someFieldValues) {
100 varfieldClass;
101
102 if ((someFieldValues['actionType'] != 'NONE') || (someFieldValues['label'] != '') && (someFieldValues['value'] != '')) {
103 if (someFieldValues['isHidden'] == true) {
104 fieldClass = 'password';
105 } else {
106 fieldClass = '';
107 }
108
109 this.append(this.fieldListElement(), {tag:'li', cls:'cardField', children:[
110 {tag:'a', href:'#', cls:fieldClass, html:someFieldValues['value'], children:[
111 {tag:'small', cls:'label', html:someFieldValues['label']}
112 ]}
113 ]})
114 }
115 },
116
117 'addField': function (aField) {
118 var deferredResult;
119 varfieldValues;
120
121 fieldValues = {};
122 deferredResult = new Clipperz.Async.Deferred("CardDetail.addField", {trace:false});
123 deferredResult.addMethod(aField, 'label');
124 deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
125 deferredResult.addMethod(aField, 'value');
126 deferredResult.addCallback(function (aValue) { fieldValues['value'] = aValue; });
127 deferredResult.addMethod(aField, 'actionType');
128 deferredResult.addCallback(function (aValue) { fieldValues['actionType'] = aValue; });
129 deferredResult.addMethod(aField, 'isHidden');
130 deferredResult.addCallback(function (aValue) { fieldValues['isHidden'] = aValue; });
131 deferredResult.addMethod(this, 'renderFieldValues', fieldValues);
132 deferredResult.callback();
133
134 return deferredResult;
135 },
136
137 //-------------------------------------------------------------------------
138
139 'directLoginElement': function () {
140 varresult;
141
142 result = this.getElement('directLoginList');
143 if (result == null) {
144 this.append(this.getElement('cardDetails'), {tag:'h2', html:"Direct login"});
145 result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('directLoginList')});
146 }
147
148 return result;
149 },
150
151 'addDirectLogin': function (aDirectLogin) {
152 this.append(this.directLoginElement(), {tag:'li', cls:'directLogin forward', children:[
153 {tag:'a', href:'#', html:"direct login", children:[
154 {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:'http://www.clipperz.com/favicon.ico'}]}
155 ]}
156 ]})
157
158console.log("ADD DIRECT LOGIN", aDirectLogin);
159 },
160
161 //=========================================================================
162
163 'showCard': function (aCard) {
164 var deferredResult;
165
166 // this.render();
167
168console.log("CardDetail.showCard", aCard);
169 deferredResult = new Clipperz.Async.Deferred("CardDetail.showCard", {trace:false});
170 deferredResult.addMethod(aCard, 'label');
171 deferredResult.addMethod(this, 'setTitle');
172
173 deferredResult.addMethod(aCard, 'fields');
174 deferredResult.addCallback(MochiKit.Base.values);
175 deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addField'));
176
177 deferredResult.addMethod(aCard, 'directLogins');
178 deferredResult.addCallback(MochiKit.Base.values);
179 deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addDirectLogin'));
180
181
182 deferredResult.callback();
183
184 return deferredResult;
185 // return Clipperz.Async.callbacks("CardDialogController.updateComponentState", [
186 // MochiKit.Base.method(this.record(), 'hasPendingChanges'),
187 // MochiKit.Base.method(this.cardDialogComponent(), 'setShouldEnableSaving'),
188 //
189 // MochiKit.Base.method(this.record(), 'label'),
190 // MochiKit.Base.method(this.cardDialogComponent(), 'setTitle'),
191 // MochiKit.Base.method(this.record(), 'notes'),
192 // MochiKit.Base.method(this.cardDialogComponent(), 'setNotes'),
193 //
194 // MochiKit.Base.method(this.record(), 'fields'),
195 // MochiKit.Base.values,
196 // MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithField')),
197//
198 // MochiKit.Base.method(this.record(), 'directLogins'),
199 // MochiKit.Base.values,
200 // MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithDirectLogin')),
201//
202 // MochiKit.Base.method(this.cardDialogComponent(), 'resetNewFieldInputs'),
203 // MochiKit.Base.noop
204 // ], {trace:false});
205
206 },
207
208 //=========================================================================
209
210 'showCardDetails': function (someData) {
211 this.element().innerHTML = '';
212 this.append(this.element(), [
213 {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) {
214 return {tag:'div', cls:'row', children:[
215 {tag:'label', html:aFieldData['label']},
216 // {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), html:aFieldData['value']}
217 {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), children:[
218 {tag:'div', children:[{tag:'p', html:aFieldData['value']}]}
219 ]}
220 // {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), value:aFieldData['value'], disabled:true}
221
222 ]}
223 }, someData['fields'])}
224 ]);
225
226 MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) {
227 MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); })
228 }, this));
229
230 if (someData['directLogins'].length > 0) {
231 this.append(this.element(), [
232 {tag:'h2', html:"Direct logins"},
233 {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) {
234 return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[
235 {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']},
236 // {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']}
237 {tag:'span', cls:'directLogin', html:aDirectLoginData['label']}
238 ]}
239 }, someData['directLogins'])}
240 ]);
241
242 MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) {
243 MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler');
244 }, this),
245 MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row'])
246 )
247 };
248
249 if (someData['notes'] != '') {
250 this.append(this.element(), [
251 {tag:'h2', html:"Notes"},
252 {tag:'fieldset', id:this.getId('fieldset'), children:[
253 {tag:'div', cls:'row notes', children:[
254 {tag:'span', html:someData['notes']}
255 ]}
256 ]}
257 ]);
258 };
259
260 return true;
261 },
262
263 //-------------------------------------------------------------------------
264/*
265 'toggleClickHandler': function (anEvent) {
266 varnextState;
267 varfieldValue;
268
269//console.log("TOGGLE");
270 anEvent.preventDefault;
271 fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0];
272
273 nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true');
274 if (nextState) {
275 MochiKit.DOM.removeElementClass(fieldValue, 'clear');
276 } else {
277 MochiKit.DOM.addElementClass(fieldValue, 'clear');
278 }
279
280 MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState);
281 },
282* /
283 //=========================================================================
284/*
285 'directLoginClickHandler': function (anEvent) {
286 anEvent.preventDefault();
287
288 if (/(directLogin_)/.test(anEvent.src().id)) {
289 var directLoginReference;
290
291 directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2];
292 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference});
293 }
294 },
295*/
296 //=========================================================================
297
298 __syntaxFix__: "syntax fix"
299});
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
@@ -2,100 +2,153 @@
2 2
3Copyright 2008-2011 Clipperz Srl 3Copyright 2008-2011 Clipperz Srl
4 4
5This file is part of Clipperz Community Edition. 5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager. 6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please 7For further information about its features and functionalities please
8refer to http://www.clipperz.com. 8refer to http://www.clipperz.com.
9 9
10* Clipperz Community Edition is free software: you can redistribute 10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public 11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version 12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version. 13 3 of the License, or (at your option) any later version.
14 14
15* Clipperz Community Edition is distributed in the hope that it will 15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied 16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details. 18 See the GNU Affero General Public License for more details.
19 19
20* You should have received a copy of the GNU Affero General Public 20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see 21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>. 22 <http://www.gnu.org/licenses/>.
23 23
24*/ 24*/
25 25
26Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components'); 26Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
27 27
28Clipperz.PM.UI.iPhone.Components.CardList = function(args) { 28Clipperz.PM.UI.Mobile.Components.CardList = function(args) {
29 args = args || {}; 29 args = args || {};
30 30
31 Clipperz.PM.UI.iPhone.Components.CardList.superclass.constructor.apply(this, arguments); 31 Clipperz.PM.UI.Mobile.Components.CardList.superclass.constructor.apply(this, arguments);
32 32
33 this._cardDetail = null; 33 this._cardDetail = null;
34 34
35 return this; 35 return this;
36} 36}
37 37
38//============================================================================= 38//=============================================================================
39 39
40Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, { 40Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, {
41 41
42 //------------------------------------------------------------------------- 42 //-------------------------------------------------------------------------
43 43
44 'toString': function () { 44 'toString': function () {
45 return "Clipperz.PM.UI.iPhone.Components.CardList component"; 45 return "Clipperz.PM.UI.Mobile.Components.CardList component";
46 }, 46 },
47 47
48 //------------------------------------------------------------------------- 48 //-------------------------------------------------------------------------
49 49
50 'renderSelf': function(/*aContainer, aPosition*/) { 50 'renderSelf': function () {
51 this.append(this.element(), [ 51 this.append(this.element(), {tag:'div', cls:'cardList', children:[
52 {tag:'div', cls:'toolbar', id:'toolbar', children:[ 52 {tag:'div', cls:'toolbar', children:[
53 {tag:'h1', id:'pageTitle', html:"cards"}, 53 {tag:'h1', html:"clipperz"},
54 {tag:'a', id:'backButton', cls:'button', href:'#', html:"cards"} 54 // {tag:'input', name:'search', type:'search', autocomplete:'off', placeholder:"search", id:this.getId('search')},
55 {tag:'a', href:'#', id:'settings', cls:'button', html:"*"}
55 ]}, 56 ]},
56 {tag:'div', cls:'cardList', id:this.getId('cardList'), children:[ 57 {tag:'div', cls:'scroll', id:this.getId('listBox'), children:[
57 {tag:'form', title:'search', cls:'panel cardListSearchForm', id:this.getId('cardListSearchForm'), children:[ 58 {tag:'ul', cls:'rounded', id:this.getId('list'), children:[
58 {tag:'input', type:'search', name:'search', value:"", placeholder:"search", id:this.getId('searchField')} 59 {tag:'li', html:'loading'}
59 ]}, 60 ]}
60 {tag:'ul', cls:'panel cardListPanel', id:this.getId('cardListPanel'), children:[]} 61 ]}
61 ]}, 62 ]});
62 {tag:'div', cls:'panel cardDetailPanel', id:this.getId('cardDetail')} 63
63 ]); 64 MochiKit.Signal.connect(this.getElement('list'), 'onclick', this, 'cardSelectionHandler');
64 65 MochiKit.Signal.connect(this.getElement('list'), 'ontouchstart',this, 'cardSelectionHandler');
65 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this,'searchHandler'); 66 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this,'searchHandler');
66 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this,'searchHandler'); 67 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this,'searchHandler');
67 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this,'searchHandler'); 68 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this,'searchHandler');
68 69
69 MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this,'cardListClickHandler'); 70 // MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this,'cardListClickHandler');
70 MochiKit.Signal.connect('backButton', 'onclick', this,'backButtonClickHandler'); 71 // MochiKit.Signal.connect('backButton', 'onclick', this,'backButtonClickHandler');
71 72
72 MochiKit.Style.hideElement('backButton'); 73 // MochiKit.Style.hideElement('backButton');
73 MochiKit.Style.hideElement(this.getElement('cardDetail')); 74 // MochiKit.Style.hideElement(this.getElement('cardDetail'));
74 }, 75 },
75 76
76 //------------------------------------------------------------------------- 77 'showCards': function (someCards) {
78 varcardListElement;
79 if (this.isFullyRendered() == false) {
80 this.render();
81 };
82
83 cardListElement = this.getElement('list')
84
85 cardInfo = {
86 '_rowObject': MochiKit.Async.succeed,
87 '_reference': MochiKit.Base.methodcaller('reference'),
88 '_searchableContent':MochiKit.Base.methodcaller('searchableContent'),
89 'label': MochiKit.Base.methodcaller('label'),
90 'favicon': MochiKit.Base.methodcaller('favicon')
91 };
92
93//console.log("someCards", someCards);
94 deferredResult = new Clipperz.Async.Deferred("CardList.showCards", {trace:false});
95 deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false}));
96 deferredResult.addCallback(Clipperz.Async.collectAll);
97 deferredResult.addCallback(MochiKit.Base.methodcaller('sort', Clipperz.Base.caseInsensitiveKeyComparator('label')));
98 deferredResult.addCallbackPass(MochiKit.DOM.replaceChildNodes, cardListElement);
99 // deferredResult.addCallbackPass(MochiKit.DOM.removeElementClass, cardListElement, 'loading');
100 deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'appendCardToList', cardListElement));
101 deferredResult.callback(someCards);
102 },
103
104 'appendCardToList': function (aCardListElement, aCardInfo) {
105//console.log("appendCardToList", aCardInfo);
106 this.append(aCardListElement, {tag:'li', cls:'cardListItem arrow', cardreference:aCardInfo['_reference'], children:[
107 {tag:'a', href:'#', html:aCardInfo['label'], children:[
108 {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:aCardInfo['favicon']}]}
109 ]}
110 ]});
111 },
112
113 'cardSelectionHandler': function (anEvent) {
114 var listElement;
115 varcardReference;
116
117 anEvent.preventDefault();
118
119 listElement = anEvent.target();
120 if (MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') == null) {
121 listElement = MochiKit.DOM.getFirstParentByTagAndClassName(anEvent.target(), tagName='li', className='cardListItem');
122 }
123 cardReference = MochiKit.DOM.getNodeAttribute(listElement, 'cardreference');
124console.log("###", listElement, cardReference);
125 //TODO: Notify card with reference MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') has been selected
126 MochiKit.Signal.signal(this, 'selectedCard', cardReference);
127 },
77 128
129 //-------------------------------------------------------------------------
130/*
78 'searchHandler': function (anEvent) { 131 'searchHandler': function (anEvent) {
79 if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ENTER')) { //RETURN 132 if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ENTER')) { //RETURN
80 anEvent.preventDefault(); 133 anEvent.preventDefault();
81 } else { 134 } else {
82 if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ESCAPE')) { 135 if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ESCAPE')) {
83 anEvent.target().value = ""; 136 anEvent.target().value = "";
84 } 137 }
85 138
86 if (anEvent.type() == 'keyup') { 139 if (anEvent.type() == 'keyup') {
87 MochiKit.Signal.signal(this, 'searchEvent', anEvent.target().value); 140 MochiKit.Signal.signal(this, 'searchEvent', anEvent.target().value);
88 } 141 }
89 } 142 }
90 }, 143 },
91 144
92 //------------------------------------------------------------------------- 145 //-------------------------------------------------------------------------
93 146
94 'update': function (someObjects) { 147 'update': function (someObjects) {
95 varcardListPanel; 148 varcardListPanel;
96 var i,c; 149 var i,c;
97 150
98 cardListPanel = this.getElement('cardListPanel'); 151 cardListPanel = this.getElement('cardListPanel');
99 cardListPanel.innerHTML = ''; 152 cardListPanel.innerHTML = '';
100 153
101 c = someObjects.length; 154 c = someObjects.length;
@@ -106,49 +159,49 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
106 {tag:'a', id:('cardListReference_' + someObjects[i]['_reference']), href:'#', html:someObjects[i]['label']} 159 {tag:'a', id:('cardListReference_' + someObjects[i]['_reference']), href:'#', html:someObjects[i]['label']}
107 ]}) 160 ]})
108 161
109 MochiKit.Signal.connect('cardListItem_' + someObjects[i]['_reference'], 'onclick', this, 'cardListClickHandler'); 162 MochiKit.Signal.connect('cardListItem_' + someObjects[i]['_reference'], 'onclick', this, 'cardListClickHandler');
110 } 163 }
111 164
112 }, 165 },
113 166
114 'cardListClickHandler': function (anEvent) { 167 'cardListClickHandler': function (anEvent) {
115 anEvent.preventDefault(); 168 anEvent.preventDefault();
116 169
117 if (/(cardListReference_|cardListItem_)/.test(anEvent.target().id)) { 170 if (/(cardListReference_|cardListItem_)/.test(anEvent.target().id)) {
118 var cardListReference; 171 var cardListReference;
119 172
120 cardListReference = anEvent.target().id.match(/(cardListReference_|cardListItem_)(.*)/)[2]; 173 cardListReference = anEvent.target().id.match(/(cardListReference_|cardListItem_)(.*)/)[2];
121//console.log("Showing detail for card named", cardListReference); 174//console.log("Showing detail for card named", cardListReference);
122 MochiKit.Signal.signal(this, 'selectedCard', cardListReference); 175 MochiKit.Signal.signal(this, 'selectedCard', cardListReference);
123 } 176 }
124 }, 177 },
125 178
126 //========================================================================= 179 //=========================================================================
127 180
128 'cardDetail': function (someData) { 181 'cardDetail': function (someData) {
129 if (this._cardDetail == null) { 182 if (this._cardDetail == null) {
130 this._cardDetail = new Clipperz.PM.UI.iPhone.Components.CardDetail({element:this.getElement('cardDetail')}); 183 this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:this.getElement('cardDetail')});
131 } 184 }
132 185
133 return this._cardDetail; 186 return this._cardDetail;
134 }, 187 },
135 188
136 //------------------------------------------------------------------------- 189 //-------------------------------------------------------------------------
137 190
138 'removeCardDetail': function () { 191 'removeCardDetail': function () {
139 if (this._cardDetail != null) { 192 if (this._cardDetail != null) {
140 this._cardDetail.remove(); 193 this._cardDetail.remove();
141 this._cardDetail = null; 194 this._cardDetail = null;
142 } 195 }
143 }, 196 },
144 197
145 //========================================================================= 198 //=========================================================================
146 199
147 'showCard': function (someData) { 200 'showCard': function (someData) {
148 vardeferredResult; 201 vardeferredResult;
149 varoffset; 202 varoffset;
150 203
151 offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480); 204 offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
152 this.cardDetail().render(); 205 this.cardDetail().render();
153 this.cardDetail().setCardReference(someData['_reference']); 206 this.cardDetail().setCardReference(someData['_reference']);
154 MochiKit.Style.setElementPosition(this.cardDetail().element(), {x:offset}); 207 MochiKit.Style.setElementPosition(this.cardDetail().element(), {x:offset});
@@ -174,28 +227,28 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
174 return this.cardDetail().showCardDetails(someData); 227 return this.cardDetail().showCardDetails(someData);
175 }, 228 },
176 229
177 //========================================================================= 230 //=========================================================================
178 231
179 'backButtonClickHandler': function (anEvent) { 232 'backButtonClickHandler': function (anEvent) {
180 varoffset; 233 varoffset;
181 234
182 anEvent.preventDefault(); 235 anEvent.preventDefault();
183 236
184 MochiKit.DOM.getElement('pageTitle').innerHTML = "cards"; 237 MochiKit.DOM.getElement('pageTitle').innerHTML = "cards";
185 238
186 offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480); 239 offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
187 MochiKit.Style.setElementPosition(this.getElement('cardList'), {x:-offset}); 240 MochiKit.Style.setElementPosition(this.getElement('cardList'), {x:-offset});
188 MochiKit.DOM.showElement(this.getElement('cardList')); 241 MochiKit.DOM.showElement(this.getElement('cardList'));
189 242
190 new MochiKit.Visual.Parallel([ 243 new MochiKit.Visual.Parallel([
191 new MochiKit.Visual.Move(this.getElement('cardList'), {x:offset, y:0, mode:'relative',transition:MochiKit.Visual.Transitions.linear, sync:true}), 244 new MochiKit.Visual.Move(this.getElement('cardList'), {x:offset, y:0, mode:'relative',transition:MochiKit.Visual.Transitions.linear, sync:true}),
192 new MochiKit.Visual.Move(this.getElement('cardDetail'), {x:offset, y:0, mode:'relative',transition:MochiKit.Visual.Transitions.linear, sync:true}), 245 new MochiKit.Visual.Move(this.getElement('cardDetail'), {x:offset, y:0, mode:'relative',transition:MochiKit.Visual.Transitions.linear, sync:true}),
193 MochiKit.Visual.fade (this.getElement('cardDetail'), { transition:MochiKit.Visual.Transitions.linear, sync:true}), 246 MochiKit.Visual.fade (this.getElement('cardDetail'), { transition:MochiKit.Visual.Transitions.linear, sync:true}),
194 MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true}) 247 MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true})
195 ], {duration:1, afterFinish:MochiKit.Base.method(this, 'removeCardDetail')}) 248 ], {duration:1, afterFinish:MochiKit.Base.method(this, 'removeCardDetail')})
196 249
197 }, 250 },
198 251*/
199 //========================================================================= 252 //=========================================================================
200 __syntaxFix__: "syntax fix" 253 __syntaxFix__: "syntax fix"
201}); 254});
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
27
28Clipperz.PM.UI.Mobile.Components.LoginForm = function(args) {
29 args = args || {};
30
31 this._pin = '';
32
33 this._message = null;
34 this._steps = 0;
35 this._actualSteps = 0;
36
37 this._callback = null;
38 this._errorCallback = null;
39
40 this._mode = 'CREDENTIALS';
41
42 Clipperz.PM.UI.Mobile.Components.LoginForm.superclass.constructor.apply(this, arguments);
43
44 return this;
45}
46
47//=============================================================================
48
49Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, {
50
51 //-------------------------------------------------------------------------
52
53 'toString': function () {
54 return "Clipperz.PM.UI.Mobile.Components.LoginForm component";
55 },
56
57 //-------------------------------------------------------------------------
58
59 'callback': function () {
60 return this._callback;
61 },
62
63 'errorCallback': function () {
64 return this._errorCallback;
65 },
66
67 //-------------------------------------------------------------------------
68
69 'mode': function () {
70 return this._mode;
71 },
72
73 'setMode': function (aValue) {
74 this._mode = aValue;
75 },
76
77 //..........................................................................
78
79 'pin': function () {
80 return this._pin;
81 },
82
83 'setPin': function (aValue) {
84 this._pin = aValue;
85 },
86
87 //..........................................................................
88
89 'username': function () {
90 return this._username;
91 },
92
93 'setUsername': function (aValue) {
94 this._username = aValue;
95 },
96
97 //..........................................................................
98
99 'passphrase': function () {
100 return this._passphrase;
101 },
102
103 'setPassphrase': function (aValue) {
104 this._passphrase = aValue;
105 },
106
107 //-------------------------------------------------------------------------
108
109 'message': function () {
110 return this._message;
111 },
112
113 '_setMessage': function (aValue) {
114 this._message = aValue;
115
116 if (aValue == null) {
117 MochiKit.Style.hideElement(this.getElement('credentialsMessage'));
118 } else {
119 this.getElement('message').innerHTML = aValue;
120 MochiKit.Style.showElement(this.getElement('credentialsMessage'));
121 }
122 },
123
124 'setMessage': function (aValue) {
125 this._setMessage(aValue);
126 MochiKit.DOM.removeElementClass(this.getElement('credentialsMessage'), 'error');
127 },
128
129 'setErrorMessage': function (aValue) {
130 this._setMessage(aValue);
131 MochiKit.DOM.addElementClass(this.getElement('credentialsMessage'), 'error');
132 },
133
134 //-------------------------------------------------------------------------
135
136 'setCallbacks': function (args) {
137 this._callback = args['callback'];
138 this._errorCallback = args['errorCallback'];
139 },
140
141 'showErrors': function (args) {
142//console.log("LoginForm.showErrors", args);
143 if (args['previousFailedAttempt'] == 'LOGIN') {
144 this.setErrorMessage("Wrong credentials");
145 } else if (args['previousFailedAttempt'] == 'PIN') {
146 if (args['failedAttempts'] == -1) {
147 this.setErrorMessage("Wrong PIN - Resetted");
148 } else {
149 this.setErrorMessage("Wrong PIN");
150 }
151 } else {
152 this.setMessage(null);
153 }
154 },
155
156 'updateWithArgs': function (args) {
157 this.renderIfNeeded();
158 this.setCallbacks(args);
159 this.showErrors(args);
160 this.updateRendering();
161 },
162
163 'showPinLogin': function (args) {
164 this.setPin('');
165 this.setMode('PIN');
166 this.updateWithArgs(args);
167
168 // $(this.getAnchor('PIN')).focus();
169 this.getElement('PIN').focus();
170 },
171
172 'showCredentialsLogin': function (args) {
173 this.setMode('CREDENTIALS');
174 this.updateWithArgs(args);
175
176 if (this.getElement('usernameField').value.length == 0) {
177 // $(this.getAnchor('usernameField')).focus();
178 this.getElement('usernameField').focus();
179 } else {
180 // $(this.getAnchor('passphraseField')).focus();
181 this.getElement('passphraseField').focus();
182 this.getElement('passphraseField').select();
183 }
184 },
185
186 //-------------------------------------------------------------------------
187
188 'renderIfNeeded': function () {
189 if (this.isFullyRendered() == false) {
190 this.render();
191 };
192 this.updateRendering();
193 },
194
195 'updateRendering': function () {
196 MochiKit.Style.showElement(this.getElement('credentialsBody'));
197 MochiKit.Style.hideElement(this.getElement('validating'));
198
199 // this.hideAllPanes();
200 MochiKit.Base.map(function (aNode) { MochiKit.Style.hideElement(aNode); }, MochiKit.Selector.findDocElements('div.credentialsBody > div'));
201 if (this.mode() == 'CREDENTIALS') {
202 selectedPanel = this.getElement('credentials')
203 } else if (this.mode() == 'PIN') {
204 selectedPanel = this.getElement('pin')
205 // this.updatePinDisplay();
206 } else {
207 throw 'Unhandled login form mode';
208 }
209 MochiKit.Style.showElement(selectedPanel);
210
211 MochiKit.Style.hideElement(this.getElement('validating'));
212 },
213
214 'renderSelf': function() {
215 var selectedPanel;
216 this.append(this.element(), {tag:'div', id:'login', children:[
217 {tag:'div', cls:'toolbar', children:[
218 {tag:'h1', html:"clipperz"}
219 ]},
220 {tag:'div', cls:'scroll', children:[
221 //==================================================================
222 {tag:'div', cls:'credentialsMessage', id:this.getId('credentialsMessage'), children:[
223 {tag:'h1', cls:'message', id:this.getId('message'), html:"Message"}
224 ]},
225 //==================================================================
226 {tag:'div', cls:'credentialsBody', id:this.getId('credentialsBody'), children:[
227 //--------------------------------------------------------------
228 {tag:'div', cls:'pin', id:this.getId('pin'), children:[
229 {tag:'form', cls:'scroll', id:this.getId('pinForm'), children:[
230 {tag:'ul', cls:'edit rounded', children:[
231 {tag:'li', children:[{tag:'input', type:'number', name:'PIN', placeholder:"PIN", id:this.getId('PIN') }]},
232 ]},
233 {tag:'a', href:'#', cls:'greenButton', id:this.getId('pinSubmitButton'), html:"Login"}
234 ]}
235 ]},
236 //--------------------------------------------------------------
237 {tag:'div', cls:'credentials', id:this.getId('credentials'), children:[
238 {tag:'form', cls:'scroll', id:this.getId('credentialsForm'), children:[
239 {tag:'ul', cls:'edit rounded', children:[
240 {tag:'li', children:[{tag:'input', type:'email', name:'name', /*value:'joe',*/ placeholder:"username", id:this.getId('usernameField') }]},
241 {tag:'li', children:[{tag:'input', type:'password', name:'passphrase', /*value:'clipperz',*/placeholder:"passphrase", id:this.getId('passphraseField') }]}
242 ]},
243 {tag:'a', href:'#', cls:'greenButton', id:this.getId('credentialsSubmitButton'), html:"Login"}
244 // {tag:'input', type:'submit', cls:'greenButton', id:this.getId('credentialsSubmitButton'), value:"Login"}
245
246 ]}
247 ]},
248 //--------------------------------------------------------------
249 ]},
250 //==================================================================
251 {tag:'div', cls:'validating', id:this.getId('validating'), children:[
252 {tag:'div', cls:'loading', children:[
253 {tag:'div', cls:'spinner', children:[
254 {tag:'div', cls:'bar01'},
255 {tag:'div', cls:'bar02'},
256 {tag:'div', cls:'bar03'},
257 {tag:'div', cls:'bar04'},
258 {tag:'div', cls:'bar05'},
259 {tag:'div', cls:'bar06'},
260 {tag:'div', cls:'bar07'},
261 {tag:'div', cls:'bar08'},
262 {tag:'div', cls:'bar09'},
263 {tag:'div', cls:'bar10'},
264 {tag:'div', cls:'bar11'},
265 {tag:'div', cls:'bar12'}
266 ]}
267 ]},
268 {tag:'div', id:this.getId('loadingMessage')},
269 {tag:'a', href:'#', cls:'grayButton', id:this.getId('loginCancelButton'), html:"Cancel"}
270 ]}
271 //==================================================================
272 ]}
273 ]});
274
275 MochiKit.Signal.connect(this.getElement('credentialsForm'), 'onsubmit', this, 'submitCredentialsHandler');
276 MochiKit.Signal.connect(this.getElement('credentialsSubmitButton'), 'onclick', this, 'submitCredentialsHandler');
277
278 MochiKit.Signal.connect(this.getElement('pinForm'), 'onsubmit', this, 'submitPinHandler');
279 MochiKit.Signal.connect(this.getElement('pinSubmitButton'), 'onclick', this, 'submitPinHandler');
280
281 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'initProgress', this, 'initProgressHandle');
282 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'updateProgress',this, 'updateProgressHandle');
283 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'advanceProgress',this, 'advanceProgressHandle');
284 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'progressDone', this, 'progressDoneHandle');
285 },
286
287 //-------------------------------------------------------------------------
288
289 'submitPinHandler': function (anEvent) {
290 varpin;
291
292 this.setMessage(null);
293 pin = this.getElement('PIN').value;
294 // $(this.getAnchor('PIN')).blur();
295 this.getElement('PIN').blur();
296
297 credentials = Clipperz.PM.PIN.credentialsWithPIN(pin);
298 this.loginWithCredentials(credentials);
299 },
300
301 'submitCredentialsHandler': function (anEvent) {
302//console.log("submitCredentialsHandler");
303 varcredentials;
304
305 this.setMessage(null);
306
307 credentials = {};
308 credentials['username'] = this.getElement('usernameField').value;
309 credentials['passphrase'] = this.getElement('passphraseField').value;
310 // $(this.getAnchor('passphraseField')).blur();
311 this.getElement('passphraseField').blur();
312
313 this.loginWithCredentials(credentials);
314 },
315
316 //-------------------------------------------------------------------------
317
318 'loginWithCredentials': function (someCredentials) {
319 varargs;
320
321 args = {};
322 args['credentials'] = someCredentials;
323 args['errorCallback'] = this.errorCallback();
324
325 MochiKit.Style.hideElement(this.getElement('credentialsBody'));
326 MochiKit.Style.showElement(this.getElement('validating'));
327
328 MochiKit.Async.callLater(0.1, this.callback(), args);
329 },
330
331 //-------------------------------------------------------------------------
332
333 'initProgressHandle': function (anEvent) {
334//console.log("** initProgressHandle", anEvent);
335 this._steps = anEvent['steps'];
336 this._actualSteps = 0;
337 },
338
339 'updateProgressHandle': function (anEvent) {
340//console.log("** updateProgressHandle", anEvent);
341 this._steps += anEvent['extraSteps'];
342 },
343
344 'advanceProgressHandle': function (anEvent) {
345//console.log("** advanceProgressHandle", anEvent);
346 this._actualSteps ++;
347//console.log("STEPS: " + this._actualSteps + "/" + this._steps);
348 },
349
350 'progressDoneHandle': function (anEvent) {
351//console.log("** progressDoneHandle", anEvent);
352 },
353
354 //-------------------------------------------------------------------------
355 __syntaxFix__: "syntax fix"
356});
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.Mobile.Controllers');
27
28Clipperz.PM.UI.Mobile.Controllers.MainController = function() {
29 this._jQTouch = null;
30 this._user = null;
31 this._proxy = null;
32 this._loginForm = null;
33 this._cardList = null;
34 this._cardDetail= null;
35
36 return this;
37}
38
39MochiKit.Base.update(Clipperz.PM.UI.Mobile.Controllers.MainController.prototype, {
40
41 'toString': function () {
42 return "Clipperz.PM.UI.Mobile.Controllers.MainController";
43 },
44
45 //-------------------------------------------------------------------------
46
47 'user': function () {
48 return this._user;
49 },
50
51 'setUser': function (aValue) {
52 this._user = aValue;
53 },
54
55 //-------------------------------------------------------------------------
56
57 'jQTouch': function () {
58 return this._jQTouch;
59 },
60
61 'setJQTouch': function (aValue) {
62 this._jQTouch = aValue;
63 },
64
65 //=========================================================================
66
67 'run': function () {
68 console.log("MainController.run");
69
70 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'doLogin', MochiKit.Base.method(this, 'doLogin'));
71 Clipperz.DOM.Helper.overwrite(MochiKit.DOM.currentDocument().body, {tag:'div', id:'jqt', children:[
72 {tag:'div', id:'loginForm'},
73 {tag:'div', id:'cardList'},
74 {tag:'div', id:'cardDetail'},
75 {tag:'div', id:'preferences'}
76 ]});
77
78 this.showLoginForm();
79
80 this.initjQTouch();
81
82
83 // this.showAddToHomeScreenBaloon();
84 // this.selectInitialProxy();
85 },
86
87 'initjQTouch': function () {
88 var jqt;
89
90 jqt = new $.jQTouch({
91 icon: 'data:image/png;charset=utf-8;base64,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',
92 // icon4: 'jqtouch4.png',
93 // startupScreen: null, //Pass a string path to a 320px x 460px startup screen for full screen apps.
94 statusBar: 'black-translucent', //Styles the status bar when running as a fullscreen app. Other options are `default`, `black`, and `black-translucent`.
95 // addGlossToIcon: true, //Set to 'false' to prevent automatic glossy button effect on icon.
96 preloadImages: false, //Pass an array of image paths to load them before page loads. Ex: `['images/link_over.png', 'images/link_select.png']`
97 fixedViewport: true, //Removes the user's ability to scale the page. Ensures the site behaves more like an application.
98 // fullScreen: true, //The website will become a fullscreen application when saved to a user's home screen. Set to `false` to disable.
99 // 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.
100 // themeSelectionSelector: '#jqt #themes ul', //???
101
102 // useAnimations: true, //Set to `false` to disable all animations.
103 // useFastTouch: true, //Removes ~350ms onClick delay when tapping a link (use in conjunction with the .tap() event) **Experimental**
104 // useTouchScroll: true, //Adds support for iOS5 scrolling. Set to false to disable. **Experimental**
105
106 cacheGetRequests: false, //Automatically caches GET requests, so subsequent taps reference the pre-loaded views. (default: true)
107
108 // 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.
109
110 // cubeSelector: '.cube', //Link selector for a cube animation.
111 // dissolveSelector: '.dissolve', //Link selector for a dissolve animation.
112 // fadeSelector: '.fade', //Link selector for a fade animation.
113 // flipSelector: '.flip', //Link selector for a 3d flip animation.
114 formSelector: null, //Sets which forms are automatically submitted via Ajax. (default: 'form')
115 // popSelector: '.pop', //Link selector for a pop animation. (default: '.pop')
116 // 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 &gt; a, a:not(.dontslide)'`, etc. (default: 'body > * > ul li a')
117 // slideupSelector: '.slideup', //Link selector for a slide up animation. (default: '.slideup')
118 // submitSelector: '.submit', //Selector which, when clicked, will submit its parent form (and close keyboard if open). (default: '.submit')
119 // swapSelector: '.swap', //Link selector for 3d swap animation. (default: '.swap')
120 // 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')
121
122 debug: false
123 });
124
125 this.setJQTouch(jqt);
126 },
127
128 //=========================================================================
129
130 'showAddToHomeScreenBaloon': function () {
131console.log(">>> showAddToHomeScreenBaloon");
132 },
133
134 //-------------------------------------------------------------------------
135
136 'selectInitialProxy': function () {
137//console.log(">>> selectInitialProxy");
138 if (this.isOnline()) {
139//console.log("--- selectInitialProxy: using default proxy");
140 this._proxy = Clipperz.PM.Proxy.defaultProxy;
141 } else {
142 if (this.hasLocalData()) {
143//console.log("--- selectInitialProxy: using local cache proxy");
144 this._proxy = new Clipperz.PM.Proxy.OfflineCache({'shouldPayTolls':false});
145 } else {
146 this.showOfflineError();
147 }
148 }
149 },
150
151 //-------------------------------------------------------------------------
152
153 'showLoginForm': function (args) {
154 args = args || {};
155
156 args['callback'] = MochiKit.Base.method(this, 'doLogin');
157
158 if (Clipperz.PM.PIN.isSet()) {
159 args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedPinLogin');
160 this.loginForm().showPinLogin(args);
161 } else {
162 args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedCredentialsLogin');
163 this.loginForm().showCredentialsLogin(args);
164 }
165 },
166
167 //.........................................................................
168
169 'handleFailedCredentialsLogin': function () {
170console.log("LOGIN FAILED");
171 this.showLoginForm({'previousFailedAttempt':'LOGIN'});
172 },
173
174 //.........................................................................
175
176 'handleFailedPinLogin': function () {
177 varfailedAttempts;
178 varstatus;
179
180 failedAttempts = Clipperz.PM.PIN.recordFailedAttempt();
181 this.showLoginForm({'previousFailedAttempt':'PIN', 'failedAttempts': failedAttempts});
182 },
183
184 //-------------------------------------------------------------------------
185
186 'doLogin': function (someArgs) {
187 var deferredResult;
188 var credentials;
189 var errorCallback;
190 var user;
191 var getPassphraseDelegate;
192
193//console.log(">>> MainController.doLogin", someArgs);
194 credentials = someArgs['credentials'];
195 errorCallback = someArgs['errorCallback'] || MochiKit.Base.noop;
196
197 getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase);
198 user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate});
199
200 deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false});
201 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4});
202 deferredResult.addCallback(MochiKit.Async.wait, 0.1);
203 deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
204 deferredResult.addMethod(user, 'login');
205 deferredResult.addCallbacks(
206 MochiKit.Base.method(this, 'processSuccessfulLogin', user),
207 errorCallback
208 );
209 deferredResult.callback();
210
211 return deferredResult;
212 },
213
214 //..........................................................................
215
216 'processSuccessfulLogin': function (aUser) {
217 var deferredResult;
218
219 deferredResult = new Clipperz.Async.Deferred('MainController.processSuccessfulLogin', {trace:false});
220 deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount');
221 // deferredResult.addMethod(this, 'removeLoginForm');
222 deferredResult.addMethod(this, 'setUser', aUser);
223 deferredResult.addMethod(this, 'setupApplication');
224 deferredResult.addMethod(this, 'runApplication');
225 deferredResult.callback();
226
227 return deferredResult;
228 },
229
230 //-------------------------------------------------------------------------
231
232 'setupApplication': function () {
233 vardeferredResult;
234
235console.log(">>> setupApplication");
236 deferredResult = new Clipperz.Async.Deferred("MainController.setupApplication", {trace:false});
237 deferredResult.addMethod(this, 'welcomeFirstTimeUser');
238 deferredResult.addMethod(this, 'showPaymentReminder');
239 deferredResult.addMethod(this, 'copyDataLocally');
240 deferredResult.callback(arguments);
241
242 return deferredResult;
243 },
244
245
246 //..........................................................................
247
248 'isFirstTimeUser': function () {
249 return false;
250 },
251
252 'welcomeFirstTimeUser': function () {
253 vardeferredResult;
254
255 deferredResult = new Clipperz.Async.Deferred('MainController.welcomeFirstTimeUser', {trace:false});
256
257 if (this.isFirstTimeUser()) {
258 deferredResult.addCallback(function () { console.log("--> welcome"); });
259 }
260 deferredResult.callback();
261
262 return deferredResult;
263 },
264
265 //..........................................................................
266
267 'shouldShowPaymentReminder': function () {
268 return true;
269 },
270
271 'showPaymentReminder': function () {
272 vardeferredResult;
273
274 deferredResult = new Clipperz.Async.Deferred('MainController.showPaymentReminder', {trace:false});
275
276 if (this.shouldShowPaymentReminder()) {
277 deferredResult.addCallback(function () { console.log("--> payment reminder"); });
278 }
279 deferredResult.callback();
280
281 return deferredResult;
282 },
283
284 //..........................................................................
285
286 'canCopyDataLocally': function () {
287 return false;
288 },
289
290 'copyDataLocally': function () {
291 vardeferredResult;
292
293 deferredResult = new Clipperz.Async.Deferred('MainController.copyDataLocally', {trace:false});
294
295 if (this.canCopyDataLocally()) {
296 deferredResult.addCallback(function () { console.log("--> copy data locally"); });
297 }
298 deferredResult.callback();
299
300 return deferredResult;
301
302 },
303
304 //-------------------------------------------------------------------------
305
306 'runApplication': function () {
307 var deferredResult;
308
309//console.log(">>> runApplication");
310 deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:true});
311 deferredResult.addMethod(this.user(), 'getRecords');
312 deferredResult.addMethod(this, 'showCards');
313 deferredResult.callback();
314
315 return deferredResult;
316 },
317
318 //=========================================================================
319
320 'showOfflineError': function (anException) {
321 alert("Error: " + anException);
322 throw anException;
323 },
324
325 //=========================================================================
326
327 'isOnline': function() {
328 return navigator.onLine;
329 },
330
331 'hasLocalData': function() {
332 return false;
333 },
334
335 //=========================================================================
336
337 'loginForm': function() {
338 if (this._loginForm == null) {
339 this._loginForm = new Clipperz.PM.UI.Mobile.Components.LoginForm({element:MochiKit.DOM.getElement('loginForm')});
340 }
341
342 return this._loginForm;
343 },
344
345 'removeLoginForm': function () {
346 if (this._loginForm != null) {
347 this._loginForm.remove();
348 this._loginForm = null;
349 }
350 },
351
352 //-------------------------------------------------------------------------
353
354 'cardList': function () {
355 if (this._cardList == null) {
356 this._cardList = new Clipperz.PM.UI.Mobile.Components.CardList({element:MochiKit.DOM.getElement('cardList')});
357 MochiKit.Signal.connect(this._cardList, 'selectedCard', this, 'selectCardHandler');
358 }
359
360 return this._cardList;
361 },
362
363 'showCards': function (someCards) {
364 this.cardList().showCards(someCards);
365 this.jQTouch().goTo('#cardList', 'slideleft');
366 },
367
368 //-------------------------------------------------------------------------
369
370 'cardDetail': function () {
371 if (this._cardDetail == null) {
372 this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:MochiKit.DOM.getElement('cardDetail')});
373 }
374
375 return this._cardDetail;
376 },
377
378 'selectCardHandler': function (aCardReference) {
379 var deferredResult;
380
381 deferredResult = new Clipperz.Async.Deferred("MainController.selectCardHandler", {trace:true});
382 deferredResult.addMethod(this.cardDetail(), 'render');
383 deferredResult.addMethod(this.jQTouch(), 'goTo', '#cardDetail', 'slideleft');
384 deferredResult.addMethod(this.user(), 'getRecord', aCardReference);
385 deferredResult.addMethod(this.cardDetail(), 'showCard');
386 deferredResult.callback();
387
388 return deferredResult;
389 },
390
391 //=========================================================================
392 __syntaxFix__: "syntax fix"
393});
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
@@ -20,126 +20,131 @@ refer to http://www.clipperz.com.
20* You should have received a copy of the GNU Affero General Public 20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see 21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>. 22 <http://www.gnu.org/licenses/>.
23 23
24*/ 24*/
25 25
26Clipperz.Base.module('Clipperz.PM.UI.Web.Components'); 26Clipperz.Base.module('Clipperz.PM.UI.Web.Components');
27 27
28Clipperz.PM.UI.Web.Components.AccountPanel = function(args) { 28Clipperz.PM.UI.Web.Components.AccountPanel = function(args) {
29 args = args || {}; 29 args = args || {};
30 30
31 Clipperz.PM.UI.Web.Components.AccountPanel.superclass.constructor.apply(this, arguments); 31 Clipperz.PM.UI.Web.Components.AccountPanel.superclass.constructor.apply(this, arguments);
32 32
33 //this._initiallySelectedTab = args.selected || 'ACCOUNT'; 33 //this._initiallySelectedTab = args.selected || 'ACCOUNT';
34 this._initiallySelectedTab = args.selected || 'PASSPHRASE'; 34 this._initiallySelectedTab = args.selected || 'PASSPHRASE';
35 this._tabPanelControllerConfiguration = { 35 this._tabPanelControllerConfiguration = {
36 // 'ACCOUNT': { 36 // 'ACCOUNT': {
37 // tab:'accountTab', 37 // tab:'accountTab',
38 // panel:'accountPanel' 38 // panel:'accountPanel'
39 // }, 39 // },
40 'PASSPHRASE': { 40 'PASSPHRASE': {
41 tab:'passphraseTab', 41 tab:'passphraseTab',
42 panel:'passphrasePanel' 42 panel:'passphrasePanel'
43 }, 43 },
44 'OTP': {
45 tab:'OTPTab',
46 panel:'OTPPanel'
47 },
44 'PREFERENCES': { 48 'PREFERENCES': {
45 tab:'preferencesTab', 49 tab:'preferencesTab',
46 panel:'preferencesPanel' 50 panel:'preferencesPanel'
47 }, 51 },
48 'LOGIN_HISTORY': { 52 'LOGIN_HISTORY': {
49 tab:'loginHistoryTab', 53 tab:'loginHistoryTab',
50 panel:'loginHistoryPanel' 54 panel:'loginHistoryPanel'
51 } 55 }
52 }; 56 };
53 57
54 return this; 58 return this;
55} 59}
56 60
57//============================================================================= 61//=============================================================================
58 62
59Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.Common.Components.TabPanelComponent, { 63Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.Common.Components.TabPanelComponent, {
60 64
61 //------------------------------------------------------------------------- 65 //-------------------------------------------------------------------------
62 66
63 'toString': function () { 67 'toString': function () {
64 return "Clipperz.PM.UI.Web.Components.AccountPanel component"; 68 return "Clipperz.PM.UI.Web.Components.AccountPanel component";
65 }, 69 },
66 70
67 //------------------------------------------------------------------------- 71 //-------------------------------------------------------------------------
68 72
69 'renderSelf': function(/*aContainer, aPosition*/) { 73 'renderSelf': function(/*aContainer, aPosition*/) {
70//Clipperz.log("AccountPanel.renderSelf element", this.element()); 74//Clipperz.log("AccountPanel.renderSelf element", this.element());
71 this.append(this.element(), [ 75 this.append(this.element(), [
72 {tag:'div', cls:'header', children:[ 76 {tag:'div', cls:'header', children:[
73 {tag:'div', cls:'subPanelTabs', children:[ 77 {tag:'div', cls:'subPanelTabs', children:[
74 {tag:'ul', children:[ 78 {tag:'ul', children:[
75 // {tag:'li', id:this.getId('accountTab'), children:[{tag:'a', href:'#', html:'Account'}], cls:'first'}, 79 // {tag:'li', id:this.getId('accountTab'), children:[{tag:'a', href:'#', html:'Account'}], cls:'first'},
76 {tag:'li', id:this.getId('passphraseTab'), children:[{tag:'a', href:'#', html:'Passphrase'}], cls:'first'}, 80 {tag:'li', id:this.getId('passphraseTab'), children:[{tag:'a', href:'#', html:'Passphrase'}], cls:'first'},
81 {tag:'li', id:this.getId('OTPTab'), children:[{tag:'a', href:'#', html:'One Time Passwords'}]},
77 {tag:'li', id:this.getId('preferencesTab'), children:[{tag:'a', href:'#', html:'Preferences'}]}, 82 {tag:'li', id:this.getId('preferencesTab'), children:[{tag:'a', href:'#', html:'Preferences'}]},
78 {tag:'li', id:this.getId('loginHistoryTab'),children:[{tag:'a', href:'#', html:'Login history'}]} 83 {tag:'li', id:this.getId('loginHistoryTab'),children:[{tag:'a', href:'#', html:'Login history'}]}
79 ]} 84 ]}
80 ]} 85 ]}
81 ]}, 86 ]},
82 {tag:'div', cls:'body', children:[ 87 {tag:'div', cls:'body', children:[
83 {tag:'div', cls:'accountPanel', children:[ 88 {tag:'div', cls:'accountPanel', children:[
84 {tag:'div', cls:'subPanelContent', children:[ 89 {tag:'div', cls:'subPanelContent', children:[
85 {tag:'ul', children:[ 90 {tag:'ul', children:[
86 // {tag:'li', id:this.getId('accountPanel'),children:[ 91 // {tag:'li', id:this.getId('accountPanel'),children:[
87 // {tag:'h3', html:"-- Account --"} 92 // {tag:'h3', html:"-- Account --"}
88 // ]}, 93 // ]},
89 {tag:'li', id:this.getId('passphrasePanel'),children:[ 94 {tag:'li', id:this.getId('passphrasePanel'),children:[
90 {tag:'h3', cls:'changePassphrase', html:"Change passphrase"}, 95 {tag:'h3', cls:'changePassphrase', html:"Change passphrase"},
91 {tag:'form', id:this.getId('changePassphrase'), cls:'changePassphrase', children:[ 96 {tag:'form', id:this.getId('changePassphrase'), cls:'changePassphrase', children:[
92 {tag:'div', cls:'currentCredentials', children:[ 97 {tag:'div', cls:'currentCredentials', children:[
93 {tag:'div', cls:'field username', children:[ 98 {tag:'div', cls:'field username', children:[
94 {tag:'label', html:"username", 'for':this.getId('currentUsername')}, 99 {tag:'label', html:"username", 'for':this.getId('currentUsername')},
95 {tag:'input', id:this.getId('currentUsername')} 100 {tag:'input', id:this.getId('currentUsername')}
96 ]}, 101 ]},
97 {tag:'div', cls:'field passphrase', children:[ 102 {tag:'div', cls:'field passphrase', children:[
98 {tag:'label', html:"passphrase", 'for':this.getId('currentPassphrase')}, 103 {tag:'label', html:"passphrase", 'for':this.getId('currentPassphrase')},
99 {tag:'input', id:this.getId('currentPassphrase')} 104 {tag:'input', id:this.getId('currentPassphrase')}
100 ]} 105 ]}
101 ]}, 106 ]},
102 {tag:'div', cls:'newPassphrase', children:[ 107 {tag:'div', cls:'newPassphrase', children:[
103 {tag:'div', cls:'field', children:[ 108 {tag:'div', cls:'field', children:[
104 {tag:'label', html:"new passphrase", 'for':this.getId('newPassphrase')}, 109 {tag:'label', html:"new passphrase", 'for':this.getId('newPassphrase')},
105 {tag:'input', id:this.getId('newPassphrase')} 110 {tag:'input', id:this.getId('newPassphrase')}
106 ]}, 111 ]},
107 {tag:'div', cls:'field', children:[ 112 {tag:'div', cls:'field', children:[
108 {tag:'label', html:"re-new passphrase", 'for':this.getId('reNewPassphrase')}, 113 {tag:'label', html:"re-new passphrase", 'for':this.getId('reNewPassphrase')},
109 {tag:'input', id:this.getId('reNewPassphrase')} 114 {tag:'input', id:this.getId('reNewPassphrase')}
110 ]} 115 ]}
111 ]}, 116 ]},
112 {tag:'div', cls:'confirm', children:[ 117 {tag:'div', cls:'confirm', children:[
113 {tag:'input', type:'checkbox', id:this.getId('confirm')}, 118 {tag:'input', type:'checkbox', id:this.getId('confirm')},
114 {tag:'label', html:"I understand that Clipperz will not be able to recover a lost passphrase", 'for':this.getId('confirm')} 119 {tag:'label', html:"I understand that Clipperz will not be able to recover a lost passphrase", 'for':this.getId('confirm')}
115 ]} 120 ]}
116 ]}, 121 ]},
117 {tag:'div', cls:'clear'}, 122 {tag:'div', cls:'clear'},
118 {tag:'div', cls:'confirmButton', id:this.getId('confirmationButton'), children:[ 123 {tag:'div', cls:'confirmButton', id:this.getId('confirmationButton'), children:[
119 {tag:'span', html:"change passphrase"} 124 {tag:'span', html:"change passphrase"}
120 ]}, 125 ]}
121 126 ]},
122 {tag:'h3', cls:'manageOTP', html:"Manage One-Time Passphrases"}, 127 {tag:'li', id:this.getId('OTPPanel'), children:[
123 {} 128 // {tag:'h3', html:"Manage One-Time Passphrases"}
124 ]}, 129 ]},
125 {tag:'li', id:this.getId('preferencesPanel'), children:[ 130 {tag:'li', id:this.getId('preferencesPanel'), children:[
126 {tag:'h3', html:"-- Preferences --"} 131 // {tag:'h3', html:"-- Preferences --"}
127 ]}, 132 ]},
128 {tag:'li', id:this.getId('loginHistoryPanel'), children:[ 133 {tag:'li', id:this.getId('loginHistoryPanel'), children:[
129 {tag:'h3', html:"-- Login History --"} 134 // {tag:'h3', html:"-- Login History --"}
130 ]} 135 ]}
131 ]} 136 ]}
132 ]} 137 ]}
133 ]} 138 ]}
134 ]}, 139 ]},
135 {tag:'div', cls:'footer'} 140 {tag:'div', cls:'footer'}
136 ]); 141 ]);
137 142
138 this.tabPanelController().setup({selected:this.initiallySelectedTab()}); 143 this.tabPanelController().setup({selected:this.initiallySelectedTab()});
139 }, 144 },
140 145
141 //------------------------------------------------------------------------- 146 //-------------------------------------------------------------------------
142 147
143 148
144 __syntaxFix__: "syntax fix" 149 __syntaxFix__: "syntax fix"
145}); 150});
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
@@ -61,48 +61,56 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.DataPanel, Clipperz.PM.UI.Com
61 61
62 'toString': function () { 62 'toString': function () {
63 return "Clipperz.PM.UI.Web.Components.DataPanel component"; 63 return "Clipperz.PM.UI.Web.Components.DataPanel component";
64 }, 64 },
65 65
66 //------------------------------------------------------------------------- 66 //-------------------------------------------------------------------------
67 67
68 'renderSelf': function(/*aContainer, aPosition*/) { 68 'renderSelf': function(/*aContainer, aPosition*/) {
69 this.append(this.element(), [ 69 this.append(this.element(), [
70 {tag:'div', cls:'header', children:[ 70 {tag:'div', cls:'header', children:[
71 {tag:'div', cls:'subPanelTabs', children:[ 71 {tag:'div', cls:'subPanelTabs', children:[
72 {tag:'ul', children:[ 72 {tag:'ul', children:[
73 {tag:'li', id:this.getId('offlineCopyTab'),children:[{tag:'a', href:'#', html:'Offline copy'}], cls:'first'}, 73 {tag:'li', id:this.getId('offlineCopyTab'),children:[{tag:'a', href:'#', html:'Offline copy'}], cls:'first'},
74 {tag:'li', id:this.getId('sharingTab'), children:[{tag:'a', href:'#', html:'Sharing'}]}, 74 {tag:'li', id:this.getId('sharingTab'), children:[{tag:'a', href:'#', html:'Sharing'}]},
75 {tag:'li', id:this.getId('importTab'), children:[{tag:'a', href:'#', html:'Import'}]}, 75 {tag:'li', id:this.getId('importTab'), children:[{tag:'a', href:'#', html:'Import'}]},
76 {tag:'li', id:this.getId('exportTab'), children:[{tag:'a', href:'#', html:'Export'}]} 76 {tag:'li', id:this.getId('exportTab'), children:[{tag:'a', href:'#', html:'Export'}]}
77 ]} 77 ]}
78 ]} 78 ]}
79 ]}, 79 ]},
80 {tag:'div', cls:'body', children:[ 80 {tag:'div', cls:'body', children:[
81 {tag:'div', cls:'accountPanel', children:[ 81 {tag:'div', cls:'accountPanel', children:[
82 {tag:'div', cls:'subPanelContent', children:[ 82 {tag:'div', cls:'subPanelContent', children:[
83 {tag:'ul', children:[ 83 {tag:'ul', children:[
84 {tag:'li', id:this.getId('offlineCopyPanel'),children:[ 84 {tag:'li', id:this.getId('offlineCopyPanel'),children:[
85 {tag:'h3', html:"Offline copy"} 85 // {tag:'h3', html:"Offline copy"},
86 {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."},
87 {tag:'a', id:this.getId('offlineCopyDownloadLink'), href:'#', html:"Download", cls:'downloadOfflineCopy'}
86 ]}, 88 ]},
87 {tag:'li', id:this.getId('sharingPanel'),children:[ 89 {tag:'li', id:this.getId('sharingPanel'),children:[
88 {tag:'h3', html:"Sharing"} 90 // {tag:'h3', html:"Sharing"}
89 ]}, 91 ]},
90 {tag:'li', id:this.getId('importPanel'), children:[ 92 {tag:'li', id:this.getId('importPanel'), children:[
91 {tag:'h3', html:"Import"} 93 // {tag:'h3', html:"Import"}
92 ]}, 94 ]},
93 {tag:'li', id:this.getId('exportPanel'), children:[ 95 {tag:'li', id:this.getId('exportPanel'), children:[
94 {tag:'h3', html:"Export"} 96 // {tag:'h3', html:"Export"}
95 ]} 97 ]}
96 ]} 98 ]}
97 ]} 99 ]}
98 ]} 100 ]}
99 ]}, 101 ]},
100 {tag:'div', cls:'footer'} 102 {tag:'div', cls:'footer'}
101 ]); 103 ]);
102 104
103 this.tabPanelController().setup({selected:this.initiallySelectedTab()}); 105 this.tabPanelController().setup({selected:this.initiallySelectedTab()});
106 MochiKit.Signal.connect(this.getId('offlineCopyDownloadLink'), 'onclick', this, 'downloadOfflineCopy');
104 }, 107 },
105 108
109 'downloadOfflineCopy': function (anEvent) {
110 anEvent.preventDefault();
111 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', anEvent.src());
112 },
113
106 //------------------------------------------------------------------------- 114 //-------------------------------------------------------------------------
107 __syntaxFix__: "syntax fix" 115 __syntaxFix__: "syntax fix"
108}); 116});
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
@@ -61,88 +61,92 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI
61 // var loginProgressElement; 61 // var loginProgressElement;
62 // 62 //
63 // loginProgressElement = MochiKit.DOM.getElement('loginProgress'); 63 // loginProgressElement = MochiKit.DOM.getElement('loginProgress');
64 // 64 //
65 // if (loginProgressElement == null) { 65 // if (loginProgressElement == null) {
66 // loginProgressElement = this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress'}, true); 66 // loginProgressElement = this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress'}, true);
67 // } 67 // }
68 68
69//console.log(">> LoginProgress.renderSelf", this.element()); 69//console.log(">> LoginProgress.renderSelf", this.element());
70 this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress', children: [ 70 this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress', children: [
71 // this.append(loginProgressElement, [ 71 // this.append(loginProgressElement, [
72 {tag:'div', cls:'header', children:[ 72 {tag:'div', cls:'header', children:[
73 {tag:'h3', id:this.getId('title'), html:"login progress"} 73 {tag:'h3', id:this.getId('title'), html:"login progress"}
74 ]}, 74 ]},
75 {tag:'div', cls:'body', children:[ 75 {tag:'div', cls:'body', children:[
76 {tag:'div', id:this.getId('progressBar')}, 76 {tag:'div', id:this.getId('progressBar')},
77 {tag:'div', id:this.getId('errorBox'), cls:'errorBox', children:[ 77 {tag:'div', id:this.getId('errorBox'), cls:'errorBox', children:[
78 // {tag:'div',cls:'img ALERT', children:[{tag:'div'}]}, 78 // {tag:'div',cls:'img ALERT', children:[{tag:'div'}]},
79 {tag:'div',cls:'img ALERT', children:[{tag:'canvas', id:this.getId('canvas')}]}, 79 {tag:'div',cls:'img ALERT', children:[{tag:'canvas', id:this.getId('canvas')}]},
80 {tag:'p', html:"Login failed"} 80 {tag:'p', html:"Login failed"}
81 ]} 81 ]}
82 ]}, 82 ]},
83 {tag:'div', cls:'footer', children:[ 83 {tag:'div', cls:'footer', children:[
84 {tag:'div', cls:'buttonArea', id:this.getId('buttonArea'), children:[ 84 {tag:'div', cls:'buttonArea', id:this.getId('buttonArea'), children:[
85 {tag:'div', cls:'button', id:this.getId('button'), children:[ 85 // {tag:'div', cls:'button', id:this.getId('button'), children:[
86 {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"} 86 // {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"}
87 ]} 87 // ]}
88 {tag:'a', cls:'button', id:this.getId('button'), html:"cancel"}
88 ]} 89 ]}
89 ]} 90 ]}
90 ]}); 91 ]});
91 // ]); 92 // ]);
92 93
93 Clipperz.PM.UI.Canvas.marks['!'](this.getElement('canvas'), "#ffffff"); 94 Clipperz.PM.UI.Canvas.marks['!'](this.getElement('canvas'), "#ffffff");
94 95
95 this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')})); 96 this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
96 MochiKit.Style.hideElement(this.getElement('errorBox')); 97 MochiKit.Style.hideElement(this.getElement('errorBox'));
97 98
98 MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler'); 99 // MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler');
100 MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'cancelEventHandler');
99 }, 101 },
100 102
101 //------------------------------------------------------------------------- 103 //-------------------------------------------------------------------------
102 104
103 'displayElement': function() { 105 'displayElement': function() {
104 return MochiKit.DOM.getElement('loginProgress'); 106 return MochiKit.DOM.getElement('loginProgress');
105 }, 107 },
106 108
107 //------------------------------------------------------------------------- 109 //-------------------------------------------------------------------------
108 110
109 'cancelEventHandler': function(anEvent) { 111 'cancelEventHandler': function(anEvent) {
110 anEvent.preventDefault(); 112 anEvent.preventDefault();
111 113
112 MochiKit.Signal.signal(this, 'cancelEvent'); 114 MochiKit.Signal.signal(this, 'cancelEvent');
113 }, 115 },
114 116
115 //------------------------------------------------------------------------- 117 //-------------------------------------------------------------------------
116 118
117 'disableCancel': function() { 119 'disableCancel': function() {
118 MochiKit.Style.hideElement(this.getElement('buttonArea')); 120 MochiKit.Style.hideElement(this.getElement('buttonArea'));
119 }, 121 },
120 122
121 //------------------------------------------------------------------------- 123 //-------------------------------------------------------------------------
122 124
123 'showErrorMessage': function() { 125 'showErrorMessage': function() {
124 this.getElement('buttonLink').innerHTML = "close"; 126 // this.getElement('buttonLink').innerHTML = "close";
127 this.getElement('button').innerHTML = "close";
128 MochiKit.DOM.addElementClass(this.getElement('button'), 'default');
125 129
126 MochiKit.Style.hideElement(this.getElement('progressBar')); 130 MochiKit.Style.hideElement(this.getElement('progressBar'));
127 131
128 this.getElement('title').innerHTML = "Error"; 132 this.getElement('title').innerHTML = "Error";
129 MochiKit.Style.showElement(this.getElement('errorBox')); 133 MochiKit.Style.showElement(this.getElement('errorBox'));
130 MochiKit.Style.showElement(this.getElement('buttonArea')); 134 MochiKit.Style.showElement(this.getElement('buttonArea'));
131 }, 135 },
132 136
133 //------------------------------------------------------------------------- 137 //-------------------------------------------------------------------------
134 138
135 'deferredHideModalAndRemove': function(someParameters, aResult) { 139 'deferredHideModalAndRemove': function(someParameters, aResult) {
136 vardeferredResult; 140 vardeferredResult;
137 141
138 deferredResult = new Clipperz.Async.Deferred("LoginProgress.deferredHideModalAndRemove", {trace:false}); 142 deferredResult = new Clipperz.Async.Deferred("LoginProgress.deferredHideModalAndRemove", {trace:false});
139 deferredResult.addMethod(this, 'deferredHideModal'); 143 deferredResult.addMethod(this, 'deferredHideModal');
140 deferredResult.addMethod(this, 'remove'); 144 deferredResult.addMethod(this, 'remove');
141 deferredResult.addCallback(function () { 145 deferredResult.addCallback(function () {
142 return aResult; 146 return aResult;
143 }); 147 });
144 deferredResult.callback(someParameters); 148 deferredResult.callback(someParameters);
145 149
146 return deferredResult; 150 return deferredResult;
147 }, 151 },
148 152
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
@@ -61,50 +61,50 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.ToolsPanel, Clipperz.PM.UI.Co
61 61
62 'toString': function () { 62 'toString': function () {
63 return "Clipperz.PM.UI.Web.Components.ToolsPanel component"; 63 return "Clipperz.PM.UI.Web.Components.ToolsPanel component";
64 }, 64 },
65 65
66 //------------------------------------------------------------------------- 66 //-------------------------------------------------------------------------
67 67
68 'renderSelf': function(/*aContainer, aPosition*/) { 68 'renderSelf': function(/*aContainer, aPosition*/) {
69 this.append(this.element(), [ 69 this.append(this.element(), [
70 {tag:'div', cls:'header', children:[ 70 {tag:'div', cls:'header', children:[
71 {tag:'div', cls:'subPanelTabs', children:[ 71 {tag:'div', cls:'subPanelTabs', children:[
72 {tag:'ul', children:[ 72 {tag:'ul', children:[
73 {tag:'li', id:this.getId('passwordGeneratorTab'),children:[{tag:'a', href:'#', html:'Password generator'}], cls:'first'}, 73 {tag:'li', id:this.getId('passwordGeneratorTab'),children:[{tag:'a', href:'#', html:'Password generator'}], cls:'first'},
74 {tag:'li', id:this.getId('bookmarkletTab'), children:[{tag:'a', href:'#', html:'Bookmarklet'}]}, 74 {tag:'li', id:this.getId('bookmarkletTab'), children:[{tag:'a', href:'#', html:'Bookmarklet'}]},
75 {tag:'li', id:this.getId('compactEditionTab'), children:[{tag:'a', href:'#', html:'Compact edition'}]}, 75 {tag:'li', id:this.getId('compactEditionTab'), children:[{tag:'a', href:'#', html:'Compact edition'}]},
76 {tag:'li', id:this.getId('httpAuthTab'), children:[{tag:'a', href:'#', html:'HTTP Auth'}]} 76 {tag:'li', id:this.getId('httpAuthTab'), children:[{tag:'a', href:'#', html:'HTTP Auth'}]}
77 ]} 77 ]}
78 ]} 78 ]}
79 ]}, 79 ]},
80 {tag:'div', cls:'body', children:[ 80 {tag:'div', cls:'body', children:[
81 {tag:'div', cls:'accountPanel', children:[ 81 {tag:'div', cls:'accountPanel', children:[
82 {tag:'div', cls:'subPanelContent', children:[ 82 {tag:'div', cls:'subPanelContent', children:[
83 {tag:'ul', children:[ 83 {tag:'ul', children:[
84 {tag:'li', id:this.getId('passwordGeneratorPanel'),children:[ 84 {tag:'li', id:this.getId('passwordGeneratorPanel'),children:[
85 {tag:'h3', html:"Password generator"} 85 // {tag:'h3', html:"Password generator"}
86 ]}, 86 ]},
87 {tag:'li', id:this.getId('bookmarkletPanel'),children:[ 87 {tag:'li', id:this.getId('bookmarkletPanel'),children:[
88 {tag:'h3', html:"Bookmarklet"} 88 // {tag:'h3', html:"Bookmarklet"}
89 ]}, 89 ]},
90 {tag:'li', id:this.getId('compactEditionPanel'), children:[ 90 {tag:'li', id:this.getId('compactEditionPanel'), children:[
91 {tag:'h3', html:"Compact edition"} 91 // {tag:'h3', html:"Compact edition"}
92 ]}, 92 ]},
93 {tag:'li', id:this.getId('httpAuthPanel'), children:[ 93 {tag:'li', id:this.getId('httpAuthPanel'), children:[
94 {tag:'h3', html:"HTTP Auth"} 94 // {tag:'h3', html:"HTTP Auth"}
95 ]} 95 ]}
96 ]} 96 ]}
97 ]} 97 ]}
98 ]} 98 ]}
99 ]}, 99 ]},
100 {tag:'div', cls:'footer'} 100 {tag:'div', cls:'footer'}
101 ]); 101 ]);
102 102
103 this.tabPanelController().setup({selected:this.initiallySelectedTab()}); 103 this.tabPanelController().setup({selected:this.initiallySelectedTab()});
104 }, 104 },
105 105
106 //------------------------------------------------------------------------- 106 //-------------------------------------------------------------------------
107 107
108 108
109 __syntaxFix__: "syntax fix" 109 __syntaxFix__: "syntax fix"
110}); 110});
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
@@ -210,51 +210,53 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
210 210
211 'run': function(args) { 211 'run': function(args) {
212 var deferredResult; 212 var deferredResult;
213 varslot; 213 varslot;
214 varpage; 214 varpage;
215 var user; 215 var user;
216 216
217 slot = args.slot; 217 slot = args.slot;
218 user = args.user; 218 user = args.user;
219 219
220 this.setUser(user); 220 this.setUser(user);
221 221
222 slot.setContent(this.appPage()); 222 slot.setContent(this.appPage());
223 223
224 this.appPage().slotNamed('userInfoBox').setContent(this.userInfoBox()); 224 this.appPage().slotNamed('userInfoBox').setContent(this.userInfoBox());
225 this.appPage().slotNamed('tabSidePanel').setContent(this.tabSidePanel()); 225 this.appPage().slotNamed('tabSidePanel').setContent(this.tabSidePanel());
226 226
227 this.appPage().slotNamed('accountPanel').setContent(this.accountPanel()); 227 this.appPage().slotNamed('accountPanel').setContent(this.accountPanel());
228 this.appPage().slotNamed('dataPanel').setContent(this.dataPanel()); 228 this.appPage().slotNamed('dataPanel').setContent(this.dataPanel());
229 this.appPage().slotNamed('toolsPanel').setContent(this.toolsPanel()); 229 this.appPage().slotNamed('toolsPanel').setContent(this.toolsPanel());
230 230
231 this.hideAllAppPageTabSlots(); 231 this.hideAllAppPageTabSlots();
232 this.appPage().showSlot(this.slotNameForTab('cards')); 232 this.appPage().showSlot(this.slotNameForTab('cards'));
233 233
234 MochiKit.Signal.connect(this.tabSidePanel(), 'tabSelected',this, 'handleTabSelected'); 234 MochiKit.Signal.connect(this.tabSidePanel(), 'tabSelected', this, 'handleTabSelected');
235 MochiKit.Signal.connect(this.tabSidePanel(), 'addCard', this, 'handleAddCard'); 235 MochiKit.Signal.connect(this.tabSidePanel(), 'addCard', this, 'handleAddCard');
236 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'addCard', this, 'handleAddCard'); 236 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'addCard', this, 'handleAddCard');
237
238 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy',this, 'handleDownloadOfflineCopy');
237 239
238 deferredResult = new Clipperz.Async.Deferred("AppController.run", {trace:false}); 240 deferredResult = new Clipperz.Async.Deferred("AppController.run", {trace:false});
239 241
240 deferredResult.addMethod(this.cardsController(), 'run', {slot:this.appPage().slotNamed('cardGrid'), user:user}); 242 deferredResult.addMethod(this.cardsController(), 'run', {slot:this.appPage().slotNamed('cardGrid'), user:user});
241 // deferredResult.addMethod(this.directLoginsController(), 'run', {slot:this.appPage().slotNamed('directLoginGrid'), user:user}); 243 // deferredResult.addMethod(this.directLoginsController(), 'run', {slot:this.appPage().slotNamed('directLoginGrid'), user:user});
242 deferredResult.addMethod(this, 'populateUserInfo'); 244 deferredResult.addMethod(this, 'populateUserInfo');
243 245
244 deferredResult.addCallback(MochiKit.Visual.ScrollTo, 'miscLinks', {duration:0}); 246 deferredResult.addCallback(MochiKit.Visual.ScrollTo, 'miscLinks', {duration:0});
245 deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'CARDS_CONTROLLER_DID_RUN'); 247 deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'CARDS_CONTROLLER_DID_RUN');
246 deferredResult.addMethod(this.tabSidePanel(), 'selectTab', 'cards'); 248 deferredResult.addMethod(this.tabSidePanel(), 'selectTab', 'cards');
247 deferredResult.callback(); 249 deferredResult.callback();
248 }, 250 },
249 251
250 //----------------------------------------------------------------------------- 252 //-----------------------------------------------------------------------------
251 253
252 'handleTabSelected': function (selectedTabName) { 254 'handleTabSelected': function (selectedTabName) {
253 var aTabName; 255 var aTabName;
254 var aSlotName; 256 var aSlotName;
255 257
256//Clipperz.log(">>> AppController.handleTabSelected", selectedTabName); 258//Clipperz.log(">>> AppController.handleTabSelected", selectedTabName);
257 this.hideAllAppPageTabSlots(); 259 this.hideAllAppPageTabSlots();
258 this.appPage().showSlot(this.slotNameForTab(selectedTabName)); 260 this.appPage().showSlot(this.slotNameForTab(selectedTabName));
259 261
260 switch (selectedTabName) { 262 switch (selectedTabName) {
@@ -300,27 +302,50 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
300 return deferredResult; 302 return deferredResult;
301 }, 303 },
302 304
303 //----------------------------------------------------------------------------- 305 //-----------------------------------------------------------------------------
304 306
305 'handleLock': function (anEvent) { 307 'handleLock': function (anEvent) {
306 return Clipperz.Async.callbacks("AppController.handleLock", [ 308 return Clipperz.Async.callbacks("AppController.handleLock", [
307 MochiKit.Base.method(this.cardsController(), 'deleteAllCleanTextData'), 309 MochiKit.Base.method(this.cardsController(), 'deleteAllCleanTextData'),
308 MochiKit.Base.method(this.user(), 'lock') 310 MochiKit.Base.method(this.user(), 'lock')
309 ], {trace:false}); 311 ], {trace:false});
310 }, 312 },
311 313
312 //............................................................................. 314 //.............................................................................
313 315
314 'handleUnlock': function (anEvent) { 316 'handleUnlock': function (anEvent) {
315 return Clipperz.Async.callbacks("AppController.handleUnock", [ 317 return Clipperz.Async.callbacks("AppController.handleUnock", [
316 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress'), 318 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress'),
317 MochiKit.Base.method(this.user(), 'login'), 319 MochiKit.Base.method(this.user(), 'login'),
318 MochiKit.Base.method(this.cardsController(), 'focus'), 320 MochiKit.Base.method(this.cardsController(), 'focus'),
319 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'progressDone'), 321 MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'progressDone'),
320 MochiKit.Base.method(this.userInfoBox(), 'unlock') 322 MochiKit.Base.method(this.userInfoBox(), 'unlock')
321 ], {trace:false}); 323 ], {trace:false});
322 }, 324 },
323 325
326 'handleDownloadOfflineCopy': function (anEvent) {
327console.log("AppController.handleDownloadOfflineCopy");
328 var downloadHref;
329
330 downloadHref = window.location.href.replace(/\/[^\/]*$/,'') + Clipperz_dumpUrl;
331
332 if (Clipperz_IEisBroken == true) {
333 window.open(downloadHref, "");
334 } else {
335 vardeferredResult;
336 var newWindow;
337
338 newWindow = window.open("", "");
339
340 deferredResult = new Clipperz.Async.Deferred("AppController.handleDownloadOfflineCopy", {trace:true});
341 deferredResult.addCallback(MochiKit.Base.method(this.user().connection(), 'message'), 'echo', {'echo':"echo"});
342 deferredResult.addCallback(function(aWindow) {
343 aWindow.location.href = downloadHref;
344 }, newWindow);
345 deferredResult.callback();
346 }
347 },
348
324 //============================================================================= 349 //=============================================================================
325 __syntaxFix__: "syntax fix" 350 __syntaxFix__: "syntax fix"
326}); 351});
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components');
27
28Clipperz.PM.UI.iPhone.Components.CardDetail = function(args) {
29 args = args || {};
30
31 Clipperz.PM.UI.iPhone.Components.CardDetail.superclass.constructor.apply(this, arguments);
32
33 this._cardReference = null;
34
35 return this;
36}
37
38//=============================================================================
39
40Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, {
41
42 //-------------------------------------------------------------------------
43
44 'toString': function () {
45 return "Clipperz.PM.UI.iPhone.Components.CardDetail component";
46 },
47
48 //-------------------------------------------------------------------------
49
50 'cardReference': function () {
51 return this._cardReference;
52 },
53
54 'setCardReference': function (aValue) {
55 this._cardReference = aValue;
56 },
57
58 //-------------------------------------------------------------------------
59
60 'renderSelf': function(/*aContainer, aPosition*/) {
61 this.append(this.element(), [
62 {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[
63 {tag:'div', id:this.getId('progressBar')} //,
64 // {tag:'h1', cls:'loading', html:"loading"}
65 ]}
66 ]);
67
68 this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
69 MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0);
70 },
71
72 //=========================================================================
73
74 'showCardDetails': function (someData) {
75 this.element().innerHTML = '';
76 this.append(this.element(), [
77 {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) {
78 return {tag:'div', cls:'row', children:[
79 {tag:'label', html:aFieldData['label']},
80 // {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), html:aFieldData['value']}
81 {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), children:[
82 {tag:'div', children:[{tag:'p', html:aFieldData['value']}]}
83 ]}
84 // {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden']? 'password' : 'text')), value:aFieldData['value'], disabled:true}
85
86 ]}
87 }, someData['fields'])}
88 ]);
89
90 MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) {
91 MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); })
92 }, this));
93
94 if (someData['directLogins'].length > 0) {
95 this.append(this.element(), [
96 {tag:'h2', html:"Direct logins"},
97 {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) {
98 return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[
99 {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']},
100 // {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']}
101 {tag:'span', cls:'directLogin', html:aDirectLoginData['label']}
102 ]}
103 }, someData['directLogins'])}
104 ]);
105
106 MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) {
107 MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler');
108 }, this),
109 MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row'])
110 )
111 };
112
113 if (someData['notes'] != '') {
114 this.append(this.element(), [
115 {tag:'h2', html:"Notes"},
116 {tag:'fieldset', id:this.getId('fieldset'), children:[
117 {tag:'div', cls:'row notes', children:[
118 {tag:'span', html:someData['notes']}
119 ]}
120 ]}
121 ]);
122 };
123
124 return true;
125 },
126
127 //-------------------------------------------------------------------------
128/*
129 'toggleClickHandler': function (anEvent) {
130 varnextState;
131 varfieldValue;
132
133//console.log("TOGGLE");
134 anEvent.preventDefault;
135 fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0];
136
137 nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true');
138 if (nextState) {
139 MochiKit.DOM.removeElementClass(fieldValue, 'clear');
140 } else {
141 MochiKit.DOM.addElementClass(fieldValue, 'clear');
142 }
143
144 MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState);
145 },
146*/
147 //=========================================================================
148
149 'directLoginClickHandler': function (anEvent) {
150 anEvent.preventDefault();
151
152 if (/(directLogin_)/.test(anEvent.src().id)) {
153 var directLoginReference;
154
155 directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2];
156 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference});
157 }
158 },
159
160 //=========================================================================
161
162 __syntaxFix__: "syntax fix"
163});
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components');
27
28Clipperz.PM.UI.iPhone.Components.LoginForm = function(args) {
29 args = args || {};
30
31 Clipperz.PM.UI.iPhone.Components.LoginForm.superclass.constructor.apply(this, arguments);
32
33 return this;
34}
35
36//=============================================================================
37
38Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, {
39
40 //-------------------------------------------------------------------------
41
42 'toString': function () {
43 return "Clipperz.PM.UI.iPhone.Components.LoginForm component";
44 },
45
46 //-------------------------------------------------------------------------
47
48 'focusOnUsername': function () {
49 this.getElement('username').focus();
50 },
51
52 //-------------------------------------------------------------------------
53
54 'username': function () {
55 return this.getElement('username').value;
56 },
57
58 'passphrase': function () {
59 return this.getElement('passphrase').value;
60 },
61
62 //-------------------------------------------------------------------------
63
64 'renderSelf': function(/*aContainer, aPosition*/) {
65 this.append(this.element(), [
66 {tag:'div', cls:'toolbar iPhoneClipperzToolbar', children:[
67 {tag:'h1', id:'pageTitle', html:'Clipperz'},
68 {tag:'a', id:'backButton', cls:'button', href:'#', html:"back"}
69 ]},
70 {tag:'form', title:'Theaters', cls:'panel toolbarlessPanel loginForm', id:this.getId('loginFormPanel'), children:[
71 {tag:'fieldset', id:this.getId('fieldset'), children:[
72 {tag:'div', cls:'row', children:[
73 {tag:'label', html:"username"},
74 {tag:'input', type:'text', name:'username', value:"", autocorrect:'off', autocapitalize:'off', id:this.getId('username')}
75 ]},
76 {tag:'div', cls:'row', children:[
77 {tag:'label', html:"passphrase"},
78 {tag:'input', type:'password', name:'passphrase', value:"", id:this.getId('passphrase')}
79 ]}
80 ]},
81 {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Login", id:this.getId('submit')}
82 ]},
83 {tag:'div', cls:'panel toolbarlessPanel loginProgressPanel', id:this.getId('loginProgressPanel'), children:[
84 {tag:'div', id:this.getId('progressBar')} //,
85 // {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Cancel", id:this.getId('cancel')}
86 ]},
87 {tag:'div', cls:'panel loginErrorPanel', id:this.getId('loginErrorPanel'), children:[
88 {tag:'div', cls:'errorMessage', id:this.getId('errorMessageBox'), children:[
89 {tag:'h2', id:this.getId('errorMessage'), html:"Login failed"}
90 ]}
91 ]}
92 ]);
93
94 MochiKit.Signal.connect(this.getElement('submit'), 'onclick',this, 'submitHandler');
95 MochiKit.Signal.connect(this.getElement('loginFormPanel'), 'onsubmit',this, 'submitHandler');
96
97 // MochiKit.Signal.connect(this.getElement('cancel'), 'onclick',this, 'cancelHandler');
98 MochiKit.Signal.connect('backButton', 'onclick',this, 'backHandler');
99
100 this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
101
102 // MochiKit.Style.hideElement(this.getElement('errorMessage'));
103
104 this.showLoginForm();
105 // MochiKit.Async.callLater(0.2, MochiKit.Base.method(this, 'focusOnUsername'));
106 },
107
108 //-------------------------------------------------------------------------
109
110 'showLoginForm': function () {
111 MochiKit.Style.showElement(this.getElement('loginFormPanel'));
112 MochiKit.Style.hideElement(this.getElement('loginProgressPanel'));
113 MochiKit.Style.hideElement(this.getElement('loginErrorPanel'));
114 MochiKit.Style.hideElement('backButton');
115 },
116
117 'slideInLoginForm': function () {
118 varoffset;
119
120 offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
121
122 MochiKit.Style.showElement(this.getElement('loginFormPanel'));
123 MochiKit.Style.setElementPosition(this.getElement('loginFormPanel'), {x:-offset, y:0});
124
125 new MochiKit.Visual.Sequence([
126 new MochiKit.Visual.Parallel([
127 new MochiKit.Visual.Move(this.getElement('loginErrorPanel'), {x:offset, y:0, mode:'relative',transition:MochiKit.Visual.Transitions.linear, sync:true}),
128 new MochiKit.Visual.Move(this.getElement('loginFormPanel'), {x:0, y:0, mode:'absolute',transition:MochiKit.Visual.Transitions.linear, sync:true}),
129 MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true})
130 ], {duration:0.5, sync:true}),
131 MochiKit.Visual.fade(this.getElement('loginErrorPanel'), {duration:0, sync:true})
132 ], {})
133 },
134
135 'showLoginProgress': function () {
136 MochiKit.Style.hideElement(this.getElement('loginFormPanel'));
137 MochiKit.Style.showElement(this.getElement('loginProgressPanel'));
138 },
139
140 'showLoginError': function (anError) {
141 this.getElement('errorMessage').innerHTML = "Login error";
142
143 MochiKit.Style.showElement('backButton');
144 MochiKit.Style.hideElement(this.getElement('loginProgressPanel'));
145 MochiKit.Style.showElement(this.getElement('loginErrorPanel'));
146 MochiKit.Style.setElementPosition(this.getElement('loginErrorPanel'), {x:0, y:45});
147 },
148
149 //-------------------------------------------------------------------------
150/*
151 'disableCancelButton': function () {
152 MochiKit.DOM.hideElement(this.getElement('cancel'));
153 },
154*/
155 //-------------------------------------------------------------------------
156
157 'submitHandler': function (anEvent) {
158 anEvent.preventDefault();
159
160 MochiKit.Signal.signal(this, 'doLogin', {'username':this.username(), 'passphrase':this.passphrase()});
161 },
162
163 'cancelHandler': function (anEvent) {
164 anEvent.preventDefault();
165
166//console.log("CANCEL");
167 },
168
169 'backHandler': function (anEvent) {
170 anEvent.preventDefault();
171
172 this.slideInLoginForm();
173 },
174
175 //-------------------------------------------------------------------------
176
177 __syntaxFix__: "syntax fix"
178});
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 @@
1/*
2
3Copyright 2008-2011 Clipperz Srl
4
5This file is part of Clipperz Community Edition.
6Clipperz Community Edition is an online password manager.
7For further information about its features and functionalities please
8refer to http://www.clipperz.com.
9
10* Clipperz Community Edition is free software: you can redistribute
11 it and/or modify it under the terms of the GNU Affero General Public
12 License as published by the Free Software Foundation, either version
13 3 of the License, or (at your option) any later version.
14
15* Clipperz Community Edition is distributed in the hope that it will
16 be useful, but WITHOUT ANY WARRANTY; without even the implied
17 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU Affero General Public License for more details.
19
20* You should have received a copy of the GNU Affero General Public
21 License along with Clipperz Community Edition. If not, see
22 <http://www.gnu.org/licenses/>.
23
24*/
25
26Clipperz.Base.module('Clipperz.PM.UI.iPhone.Controllers');
27
28 //Some parts of this controller have been derived from the iUI library.
29
30Clipperz.PM.UI.iPhone.Controllers.MainController = function() {
31 this._loginForm = null;
32 this._cardList = null;
33 this._cachedValues =null;
34 this._user = null;
35
36 if (typeof window.onorientationchange == 'object') {
37 MochiKit.Signal.connect(window, 'onorientationchange', this, 'orientationChangeHandler');
38 MochiKit.Async.callLater(0, MochiKit.Base.method(this, 'orientationChangeHandler'));
39 } else {
40 this.setOrientation('portrait');
41 // this.setOrientation('landscape');
42 }
43
44 this.addMetaTag('viewport', 'width=devicewidth; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;');
45 this.addMetaTag('apple-mobile-web-app-capable', 'yes');
46 this.addMetaTag('apple-mobile-web-app-status-bar-style', 'black');
47
48 this.addLinkTag('apple-touch-icon', 'data:image/png;charset=utf-8;base64,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');
49 //this.addLinkTag('apple-touch-startup-image', 'default.png');
50
51 // if (!window.navigator.standalone)// not running as an installed app
52
53 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', this, 'selectedDirectLoginHandler');
54
55 MochiKit.DOM.addElementClass(document.body, 'iPhone');
56 return this;
57}
58
59MochiKit.Base.update(Clipperz.PM.UI.iPhone.Controllers.MainController.prototype, {
60
61 'toString': function () {
62 return "Clipperz.PM.UI.iPhone.Controllers.MainController";
63 },
64
65 //=========================================================================
66
67 'user': function () {
68 return this._user;
69 },
70
71 'setUser': function (aValue) {
72 this._user = aValue;
73 },
74
75 //=========================================================================
76
77 'loginForm': function() {
78 if (this._loginForm == null) {
79 MochiKit.DOM.removeElement('mainDiv');
80 this._loginForm = new Clipperz.PM.UI.iPhone.Components.LoginForm({element:MochiKit.DOM.currentDocument().body});
81 MochiKit.Signal.connect(this._loginForm, 'doLogin', this, 'doLoginHandler')
82 }
83
84 return this._loginForm;
85 },
86
87 'removeLoginForm': function () {
88 if (this._loginForm != null) {
89 this._loginForm.remove();
90 this._loginForm = null;
91 }
92 },
93
94 //-----------------------------------------------------------------------------
95
96 'cardList': function () {
97 if (this._cardList == null) {
98 this._cardList = new Clipperz.PM.UI.iPhone.Components.CardList({element:MochiKit.DOM.currentDocument().body});
99 MochiKit.Signal.connect(this._cardList, 'searchEvent',this, 'searchEventHandler')
100 MochiKit.Signal.connect(this._cardList, 'selectedCard',this, 'selecetedCardHandler')
101 }
102
103 return this._cardList;
104 },
105
106 //=========================================================================
107
108 'currentWidth': function () {
109 return this._currentWidth;
110 },
111
112 'setCurrentWidth': function (aValue) {
113 this._currentWidth = aValue;
114 },
115
116 //=========================================================================
117
118 'orientationChangeHandler': function () {
119 switch(window.orientation) {
120 case 0:
121 this.setOrientation('portrait');
122 break;
123 case 90:
124 case -90:
125 this.setOrientation('landscape');
126 break;
127 }
128 },
129
130 //-------------------------------------------------------------------------
131
132 'setOrientation': function (anOrientation) {
133 document.body.setAttribute('orientation', anOrientation);
134 setTimeout(scrollTo, 100, 0, 1);
135 },
136
137 //-------------------------------------------------------------------------
138
139 'slidePages': function (fromPage, toPage, backwards) {
140 var axis;
141 var slideDone;
142
143 slideDone = function () {
144 // console.log("slideDone");
145 if (!hasClass(toPage, "dialog")) {
146 fromPage.removeAttribute("selected");
147 }
148 checkTimer = setInterval(checkOrientAndLocation, 300);
149 setTimeout(updatePage, 0, toPage, fromPage);
150 fromPage.removeEventListener('webkitTransitionEnd', slideDone, false);
151 }
152
153 axis = (backwards ? fromPage : toPage).getAttribute("axis");
154
155 clearInterval(checkTimer);
156
157 if (canDoSlideAnim() && axis != 'y') {
158 slide2(fromPage, toPage, backwards, slideDone);
159 } else {
160 slide1(fromPage, toPage, backwards, axis, slideDone);
161 }
162 },
163
164 //-------------------------------------------------------------------------
165
166 'getCachedValues': function () {
167 var deferredResult;
168
169 if (this._cachedObjects != null) {
170 deferredResult = MochiKit.Async.succeed(this._cachedObjects);
171 } else {
172 deferredResult = new Clipperz.Async.Deferred("MainController.getCachedValues", {trace:false});
173 deferredResult.addMethod(this.user(), 'getRecords');
174 deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.getCachedValues - collectResults", {
175 '_rowObject': MochiKit.Async.succeed,
176 '_reference': MochiKit.Base.methodcaller('reference'),
177 'label': MochiKit.Base.methodcaller('label'),
178 'favicon': MochiKit.Base.methodcaller('favicon'),
179 '_searchableContent':MochiKit.Base.methodcaller('searchableContent')
180 }, {trace:false}));
181 deferredResult.addCallback(Clipperz.Async.collectAll);
182 deferredResult.addCallback(MochiKit.Base.bind(function (someRows) {
183 this._cachedObjects = someRows;
184 return this._cachedObjects;
185 }, this));
186 deferredResult.callback();
187 }
188
189 return deferredResult;
190 },
191 //=========================================================================
192
193 'run': function(shouldShowRegistrationForm) {
194 this.loginForm().render();
195 MochiKit.Async.callLater(1, MochiKit.Base.method(this.loginForm(), 'focusOnUsername'));
196 },
197
198 //=========================================================================
199
200 'doLoginHandler': function (someArgs) {
201 var deferredResult;
202 varparameters;
203 varshouldUseOTP;
204 // var loginProgress;
205 varuser;
206 var getPassphraseDelegate;
207
208 parameters = someArgs;
209 shouldUseOTP = (typeof(parameters.passphrase) == 'undefined');
210
211 getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, parameters.passphrase);
212 user = new Clipperz.PM.DataModel.User({'username':parameters.username, 'getPassphraseFunction':getPassphraseDelegate});
213
214 deferredResult = new Clipperz.Async.Deferred("MainController.doLogin", {trace:false});
215 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4});
216 deferredResult.addMethod(this.loginForm(), 'showLoginProgress');
217 deferredResult.addCallback(MochiKit.Async.wait, 0.1);
218 deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
219 deferredResult.addMethod(user, 'login');
220 deferredResult.addMethod(this, 'setUser', user);
221 deferredResult.addMethod(user, 'getRecords');
222 deferredResult.addMethod(this, 'removeLoginForm');
223 deferredResult.addMethod(this.cardList(), 'render');
224 deferredResult.addMethod(this, 'displaySelectedRecords', '');
225 deferredResult.addErrback(MochiKit.Base.method(this.loginForm(), 'showLoginError'));
226 deferredResult.callback();
227
228 return deferredResult;
229 },
230
231 //=========================================================================
232
233 'searchEventHandler': function (aValue) {
234//console.log("searching for ... " + aValue);
235 return this.displaySelectedRecords(aValue);
236 },
237
238 //=========================================================================
239
240 '_displaySelectedRows': function (aFilter, someRows) {
241 var result;
242
243 result = someRows;
244
245 if (aFilter != null) {
246 var filter;
247 varfilterRegExp;
248
249 filter = aFilter.replace(/[^A-Za-z0-9]/g, "\\$&");
250 filterRegExp = new RegExp(filter, "i");
251 result = MochiKit.Base.filter(function (aCachedResult) { return filterRegExp.test(aCachedResult['_searchableContent'])}, result);
252 }
253
254
255 result.sort(MochiKit.Base.partial(function (aKey, aComparator, aObject, bObject) {
256 return aComparator(aObject[aKey], bObject[aKey]);
257 }, 'label', Clipperz.Base.caseInsensitiveCompare));
258
259 this.cardList().update(result);
260 },
261
262 //-------------------------------------------------------------------------
263
264 'displaySelectedRecords': function (aFilter) {
265 return Clipperz.Async.callbacks("MainController.displaySelectedrows", [
266 MochiKit.Base.method(this, 'getCachedValues'),
267 MochiKit.Base.method(this, '_displaySelectedRows', aFilter)
268 ], {trace:false});
269 },
270
271 //=========================================================================
272
273 'selecetedCardHandler': function (aRecordReference) {
274 vardeferredResult;
275 varrecordData;
276
277 recordData = {};
278//console.log("Showing detail for card with reference", aRecordReference);
279 deferredResult = new Clipperz.Async.Deferred("MainController.selectedCardHandler", {trace:false});
280 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
281 deferredResult.collectResults({
282 '_reference':MochiKit.Base.methodcaller('reference'),
283 'title': MochiKit.Base.methodcaller('label'),
284 'favicon': MochiKit.Base.methodcaller('favicon')
285 });
286 deferredResult.addCallback(function (someData) {
287 MochiKit.Base.update(recordData, someData);
288 })
289 deferredResult.addMethod(this.cardList(), 'showCard', recordData);
290
291 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
292 deferredResult.addMethodcaller('notes');
293 deferredResult.addCallback(function (someNotes) {
294 recordData['notes'] = someNotes;
295 })
296
297 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
298 deferredResult.addMethodcaller('getCurrentRecordVersion');
299 deferredResult.addMethodcaller('fields');
300 deferredResult.addCallback(MochiKit.Base.values);
301 deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - fields", {
302 'label':MochiKit.Base.methodcaller('label'),
303 'value':MochiKit.Base.methodcaller('value'),
304 'isHidden':MochiKit.Base.methodcaller('isHidden')
305 }, {trace:false}));
306 deferredResult.addCallback(Clipperz.Async.collectAll);
307 deferredResult.addCallback(function (someData) {
308 recordData['fields'] = someData;
309 });
310
311 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
312 deferredResult.addMethodcaller('directLogins');
313 deferredResult.addCallback(MochiKit.Base.values);
314 deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - directLogins", {
315 'label': MochiKit.Base.methodcaller('label'),
316 'favicon': MochiKit.Base.methodcaller('favicon'),
317 '_reference':MochiKit.Base.methodcaller('reference')
318 }, {trace:false}));
319 deferredResult.addCallback(Clipperz.Async.collectAll);
320 deferredResult.addCallback(function (someData) {
321 recordData['directLogins'] = someData;
322 });
323
324 deferredResult.addMethod(this.cardList(), 'showCardDetails', recordData);
325 deferredResult.callback();
326
327 return deferredResult;
328 },
329
330 //=========================================================================
331
332 'selectedDirectLoginHandler': function (someData) {
333 vardeferredResult;
334
335//console.log("<<< signal - directLogin");
336 deferredResult = new Clipperz.Async.Deferred("MainController.selectedDirectLoginHandler", {trace:false});
337 deferredResult.addMethod(this.user(), 'getRecord', someData['cardReference']);
338 deferredResult.addMethodcaller('directLogins');
339 deferredResult.addCallback(MochiKit.Base.itemgetter(someData['directLoginReference']));
340 // deferredResult.addMethodcaller('runDirectLogin');
341 deferredResult.addCallback(Clipperz.PM.UI.Common.Controllers.DirectLoginRunner.openDirectLogin);
342 deferredResult.callback();
343
344 return deferredResult;
345 },
346
347 //=========================================================================
348
349 'addMetaTag': function (aName, aContent) {
350 varmetaTag;
351
352 metaTag = document.createElement('meta');
353 metaTag.name = aName;
354 metaTag.content = aContent;
355 document.getElementsByTagName('head')[0].appendChild(metaTag);
356 },
357
358 'addLinkTag': function (aRel, anHref) {
359 var linkTag;
360
361 linkTag = document.createElement('link');
362 linkTag.rel = aRel;
363 linkTag.href = anHref;
364 document.getElementsByTagName('head')[0].appendChild(linkTag);
365 },
366
367 //=========================================================================
368 __syntaxFix__: "syntax fix"
369}); \ No newline at end of file