summaryrefslogtreecommitdiff
path: root/frontend/gamma/js
Unidiff
Diffstat (limited to 'frontend/gamma/js') (more/less context) (show whitespace changes)
-rw-r--r--frontend/gamma/js/Clipperz/Async.js0
-rw-r--r--frontend/gamma/js/Clipperz/Base.js5
-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.js32
-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)109
-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.js15
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js16
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js14
-rw-r--r--frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js5
-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.js25
-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
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.actionsheet.js159
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.autotitles.js52
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.floaty.js96
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.location.js51
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.menusheet.js137
-rw-r--r--frontend/gamma/js/JQTouch/extensions/jqt.offline.js97
-rwxr-xr-xfrontend/gamma/js/JQTouch/extensions/jqt.themeswitcher.js123
-rw-r--r--frontend/gamma/js/JQTouch/jqtouch.js889
-rw-r--r--frontend/gamma/js/Zepto/ajax.js285
-rw-r--r--frontend/gamma/js/Zepto/assets.js21
-rw-r--r--frontend/gamma/js/Zepto/data.js67
-rw-r--r--frontend/gamma/js/Zepto/detect.js42
-rw-r--r--frontend/gamma/js/Zepto/event.js248
-rw-r--r--frontend/gamma/js/Zepto/form.js40
-rw-r--r--frontend/gamma/js/Zepto/fx.js102
-rw-r--r--frontend/gamma/js/Zepto/fx_methods.js71
-rw-r--r--frontend/gamma/js/Zepto/gesture.js35
-rw-r--r--frontend/gamma/js/Zepto/polyfill.js36
-rw-r--r--frontend/gamma/js/Zepto/selector.js81
-rw-r--r--frontend/gamma/js/Zepto/stack.js22
-rw-r--r--frontend/gamma/js/Zepto/touch.js113
-rw-r--r--frontend/gamma/js/Zepto/zepto.js751
-rw-r--r--frontend/gamma/js/main.mobile.js (renamed from frontend/gamma/js/main_iPhone.js)13
45 files changed, 5012 insertions, 797 deletions
diff --git a/frontend/gamma/js/Clipperz/Async.js b/frontend/gamma/js/Clipperz/Async.js
index 97d8ecf..f7a9517 100644
--- a/frontend/gamma/js/Clipperz/Async.js
+++ b/frontend/gamma/js/Clipperz/Async.js
diff --git a/frontend/gamma/js/Clipperz/Base.js b/frontend/gamma/js/Clipperz/Base.js
index 76b2c3f..9d399d9 100644
--- a/frontend/gamma/js/Clipperz/Base.js
+++ b/frontend/gamma/js/Clipperz/Base.js
@@ -77,6 +77,11 @@ MochiKit.Base.update(Clipperz.Base, {
77 return MochiKit.Base.compose(function(aResult) { return -aResult; }, aComparator); 77 return MochiKit.Base.compose(function(aResult) { return -aResult; }, aComparator);
78 }, 78 },
79 79
80 'caseInsensitiveKeyComparator': function (aKey) {
81 return function (a, b) {
82 return MochiKit.Base.compare(a[aKey].toLowerCase(), b[aKey].toLowerCase());
83 }
84 },
80 //------------------------------------------------------------------------- 85 //-------------------------------------------------------------------------
81/* 86/*
82 'dependsOn': function(module, deps) { 87 'dependsOn': function(module, deps) {
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
@@ -41,6 +41,7 @@ Clipperz.PM.Connection = function (args) {
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}
@@ -146,6 +147,16 @@ MochiKit.Logging.logError("### Connection.defaultErrorHandler: " + anErrorString
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: ?????
@@ -320,13 +331,12 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
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');
@@ -399,6 +409,13 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
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');
@@ -429,12 +446,19 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
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);
@@ -449,8 +473,7 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
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));
@@ -587,6 +610,7 @@ Clipperz.PM.Connection.SRP['1.1'].prototype = MochiKit.Base.update(new Clipperz.
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
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
@@ -44,7 +44,7 @@ Clipperz.PM.DataModel.User = function (args) {
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 = {
@@ -93,7 +93,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
93 }, 93 },
94 94
95 //------------------------------------------------------------------------- 95 //-------------------------------------------------------------------------
96 96/*
97 'serverLockValue': function () { 97 'serverLockValue': function () {
98 return this._serverLockValue; 98 return this._serverLockValue;
99 }, 99 },
@@ -101,7 +101,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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 () {
@@ -220,8 +220,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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'));
@@ -247,7 +247,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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
@@ -329,7 +329,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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
@@ -662,8 +662,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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 },
@@ -740,7 +740,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
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;
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
@@ -406,6 +406,10 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.BaseComponent, /*Ext.Compo
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) {
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
@@ -62,7 +62,8 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Com
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()}
@@ -70,32 +71,39 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Com
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 },
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
@@ -98,14 +98,29 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.SimpleMessagePanel, Clippe
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 () {
@@ -132,17 +147,20 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.SimpleMessagePanel, Clippe
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();
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
@@ -23,12 +23,12 @@ refer to http://www.clipperz.com.
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
@@ -37,44 +37,97 @@ Clipperz.PM.UI.iPhone.Components.CardList = function(args) {
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');
65 MochiKit.Signal.connect(this.getElement('list'), 'ontouchstart',this, 'cardSelectionHandler');
66 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this,'searchHandler');
67 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this,'searchHandler');
68 // MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this,'searchHandler');
69
70 // MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this,'cardListClickHandler');
71 // MochiKit.Signal.connect('backButton', 'onclick', this,'backButtonClickHandler');
72
73 // MochiKit.Style.hideElement('backButton');
74 // MochiKit.Style.hideElement(this.getElement('cardDetail'));
75 },
64 76
65 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this,'searchHandler'); 77 'showCards': function (someCards) {
66 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this,'searchHandler'); 78 varcardListElement;
67 MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this,'searchHandler'); 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 },
68 103
69 MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this,'cardListClickHandler'); 104 'appendCardToList': function (aCardListElement, aCardInfo) {
70 MochiKit.Signal.connect('backButton', 'onclick', this,'backButtonClickHandler'); 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;
71 116
72 MochiKit.Style.hideElement('backButton'); 117 anEvent.preventDefault();
73 MochiKit.Style.hideElement(this.getElement('cardDetail')); 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);
74 }, 127 },
75 128
76 //------------------------------------------------------------------------- 129 //-------------------------------------------------------------------------
77 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();
@@ -127,7 +180,7 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
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;
@@ -195,7 +248,7 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
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
@@ -41,6 +41,10 @@ Clipperz.PM.UI.Web.Components.AccountPanel = function(args) {
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'
@@ -74,6 +78,7 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.
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 ]}
@@ -117,16 +122,16 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.
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"}
125 ]}
120 ]}, 126 ]},
121 127 {tag:'li', id:this.getId('OTPPanel'), children:[
122 {tag:'h3', cls:'manageOTP', html:"Manage One-Time Passphrases"}, 128 // {tag:'h3', html:"Manage One-Time Passphrases"}
123 {}
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 ]}
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
@@ -82,16 +82,18 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.DataPanel, Clipperz.PM.UI.Com
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 ]}
@@ -101,6 +103,12 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.DataPanel, Clipperz.PM.UI.Com
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');
107 },
108
109 'downloadOfflineCopy': function (anEvent) {
110 anEvent.preventDefault();
111 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', anEvent.src());
104 }, 112 },
105 113
106 //------------------------------------------------------------------------- 114 //-------------------------------------------------------------------------
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
@@ -82,9 +82,10 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI
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 ]});
@@ -95,7 +96,8 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI
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 //-------------------------------------------------------------------------
@@ -121,7 +123,9 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI
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
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js
index a25c8f5..dcf506f 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/PageFooter.js
@@ -49,12 +49,11 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.PageFooter, Clipperz.PM.UI.Co
49 this.append(this.element(), [ 49 this.append(this.element(), [
50 {tag:'div', cls:'footerWrapper', children:[ 50 {tag:'div', cls:'footerWrapper', children:[
51 {tag:'div', cls:'footerContent', children:[ 51 {tag:'div', cls:'footerContent', children:[
52 // {tag:'div', cls:'footerStarIcon'},
53 {tag:'canvas', id:this.getId('footerStarIcon'), cls:'footerStarIcon'}, 52 {tag:'canvas', id:this.getId('footerStarIcon'), cls:'footerStarIcon'},
54 {tag:'span', cls:'copyright', html:'Copyright &copy; 2009-2012 Clipperz Srl'}, 53 {tag:'span', cls:'copyright', html:'Copyright &copy; 2009-2013 Clipperz Srl'},
55 {tag:'a', href:'http://www.clipperz.com/terms_of_service',target:'_blank', html:'terms of service'}, 54 {tag:'a', href:'http://www.clipperz.com/terms_of_service',target:'_blank', html:'terms of service'},
56 {tag:'a', href:'http://www.clipperz.com/privacy_policy',target:'_blank', html:'privacy policy'}, 55 {tag:'a', href:'http://www.clipperz.com/privacy_policy',target:'_blank', html:'privacy policy'},
57 {tag:'span', cls:'applicationVersion', html:'application version: [1992]'} 56 {tag:'div', cls:'applicationVersion', htmlString:'application version: <a href="https://github.com/clipperz/password-manager/tree/' + Clipperz_version + '" target="github">' + Clipperz_version + '</a>'}
58 ]} 57 ]}
59 ]} 58 ]}
60 ]); 59 ]);
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
@@ -82,16 +82,16 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.ToolsPanel, Clipperz.PM.UI.Co
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 ]}
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
@@ -235,6 +235,8 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
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 237
238 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy',this, 'handleDownloadOfflineCopy');
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});
@@ -321,6 +323,29 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
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
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.actionsheet.js b/frontend/gamma/js/JQTouch/extensions/jqt.actionsheet.js
new file mode 100644
index 0000000..2a5f8d1
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.actionsheet.js
@@ -0,0 +1,159 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
12
13 (c) 2012 by jQTouch project members.
14 See LICENSE.txt for license.
15
16 Author: Thomas Yip
17*/
18
19/*
20
21 _/ _/_/ _/_/_/_/_/ _/
22 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
23 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
24 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
25 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
26 _/
27 _/
28
29 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
30
31 (c) 2012 by jQTouch project members.
32 See LICENSE.txt for license.
33
34 Author: Thomas Yip
35*/
36
37(function($) {
38 var src = $("head script").last().attr("src") || '';
39 var scriptpath = src.split('?')[0].split('/').slice(0, -1).join('/')+'/';
40 var csspath = scriptpath + 'jqt.actionsheet.css';
41 var link = $('<link href="' + csspath + '" rel="stylesheet">');
42 $('head').append($(link));
43
44 function hide(callback) {
45 var $target = $(this);
46 var data = $(this).data('actionsheet');
47 var $source = data.source;
48
49 var timeout;
50
51 function cleanup() {
52 clearTimeout(timeout);
53
54 $source.removeClass('transition');
55 $target.removeClass('inmotion transition');
56 !callback || callback.apply(this, arguments);
57 };
58 timeout = setTimeout(cleanup, 500);
59
60 if (data.shown) {
61 $(this).data('actionsheet', {});
62 $target.one('webkitTransitionEnd', cleanup);
63
64 $source.addClass('transition');
65 $target.removeClass('current').addClass('inmotion transition');
66 $('#jqt').removeClass('actionopened');
67 }
68 return $target;
69 }
70
71 function show(callback) {
72 var $target = $(this);
73 var data = $(this).data('actionsheet') || {};
74 if (!data.shown) {
75 var $source = $('#jqt .current:not(.actionsheet)');
76
77 $target.one('webkitTransitionEnd', function() {
78 $source.removeClass('transition');
79 $target.removeClass('inmotion transition');
80 !callback || callback.apply(this, arguments);
81 });
82
83 data.shown = true;
84 data.source = $source;
85 $(this).data('actionsheet', data);
86
87 $source.addClass('transition');
88 $target.addClass('inmotion transition');
89 $('#jqt').addClass('actionopened');
90 setTimeout(function() {
91 $target.addClass('current');
92 }, 50);
93 }
94 return $target;
95 }
96
97 var methods = {
98 init: function(options) {
99 $(this).addClass('actionsheet');
100 $(this).data({shown: false});
101 },
102 show: show,
103 hide: hide
104 };
105
106 $.fn.actionsheet = function(method) {
107 if (methods[method]) {
108 if ($(this).is('.actionsheet')) {
109 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
110 } else {
111 var msg = 'Target is not a `actionsheet`. Action `' + method + '` is ignored.';
112 console.warn(msg);
113 }
114 } else if ( typeof method === 'object' || ! method ) {
115 return methods.init.apply(this, arguments);
116 } else {
117 $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
118 }
119 };
120
121 if ($.jQTouch) {
122 $.jQTouch.addTapHandler({
123 name: 'open-actionsheet',
124 isSupported: function(e, params) {
125 return params.$el.is('.action');
126 },
127 fn: function(e, params) {
128 params.$el.removeClass('active');
129
130 var $target = $(params.hash);
131 $target.actionsheet('show');
132
133 return false;
134 }
135 });
136 $.jQTouch.addTapHandler({
137 name: 'follow-actionlink',
138 isSupported: function(e, params) {
139 if ($('#jqt').hasClass('actionopened')) {
140 return params.$el.is('.actionsheet a');
141 }
142 return false;
143 },
144 fn: function(e, params) {
145 params.$el.removeClass('active');
146
147 var $target = params.$el.closest('.actionsheet');
148 $target.actionsheet('hide', function() {
149 if (!params.$el.is('.dismiss')) {
150 params.$el.trigger('tap');
151 }
152 });
153 return false;
154 }
155 });
156 } else {
157 console.error('Extension `jqt.actionsheet` failed to load. jQT not found');
158 }
159})($);
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.autotitles.js b/frontend/gamma/js/JQTouch/extensions/jqt.autotitles.js
new file mode 100644
index 0000000..94f3d9b
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.autotitles.js
@@ -0,0 +1,52 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Created by David Kaneda <http://www.davidkaneda.com>
12 Maintained by Thomas Yip <http://beedesk.com/>
13 Sponsored by Sencha Labs <http://www.sencha.com/>
14 Special thanks to Jonathan Stark <http://www.jonathanstark.com/>
15
16 Documentation and issue tracking on GitHub <http://github.com/senchalabs/jQTouch/>
17
18 (c) 2009-2011 Sencha Labs
19 jQTouch may be freely distributed under the MIT license.
20
21*/
22
23(function($) {
24 if ($.jQTouch)
25 {
26 $.jQTouch.addExtension(function AutoTitles(jQT){
27
28 var titleSelector='.toolbar h1';
29
30 $(function(){
31 $('#jqt').bind('pageAnimationStart', function(e, data){
32 if (data.direction === 'in'){
33 var $title = $(titleSelector, $(e.target));
34 var $ref = $(e.target).data('referrer');
35 if ($title.length && $ref && $title.text() === ''){
36 $title.html($ref.text());
37 }
38 }
39 });
40 });
41
42 function setTitleSelector(ts){
43 titleSelector=ts;
44 }
45
46 return {
47 setTitleSelector: setTitleSelector
48 };
49
50 });
51 }
52})($);
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.floaty.js b/frontend/gamma/js/JQTouch/extensions/jqt.floaty.js
new file mode 100644
index 0000000..c7e4485
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.floaty.js
@@ -0,0 +1,96 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Created by David Kaneda <http://www.davidkaneda.com>
12 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
13
14 Special thanks to Jonathan Stark <http://jonathanstark.com/>
15 and pinch/zoom <http://www.pinchzoom.com/>
16
17 (c) 2009 by jQTouch project members.
18 See LICENSE.txt for license.
19
20*/
21
22(function($) {
23 if ($.jQTouch)
24 {
25 $.jQTouch.addExtension(function Floaty(jQT){
26
27 $.fn.makeFloaty = function(options){
28 var defaults = {
29 align: 'top',
30 spacing: 20,
31 time: '.3s'
32 };
33
34 var settings = $.extend({}, defaults, options);
35
36 settings.align = (settings.align == 'top') ? 'top' : 'bottom';
37
38 return this.each(function(){
39 var $el = $(this);
40
41 $el.css({
42 '-webkit-transition': 'top ' + settings.time + ' ease-in-out',
43 'display': 'block',
44 'min-height': '0 !important'
45 }).data('settings', settings);
46
47 $(document).scroll(function(){
48 if ($el.data('floatyVisible') === 'true')
49 {
50 $el.scrollFloaty();
51 }
52 });
53 $el.scrollFloaty();
54 });
55 };
56
57 $.fn.scrollFloaty = function(){
58
59
60 return this.each(function(){
61 var $el = $(this);
62 var settings = $el.data('settings'); // Settings not being set as object w/Zepto
63 var wHeight = $('html').attr('clientHeight'); // WRONG
64
65 var newY = window.pageYOffset +
66 ((settings.align == 'top') ?
67 settings.spacing : wHeight - settings.spacing - $el.get(0).offsetHeight);
68
69 $el.css('top', newY).data('floatyVisible', true);
70 });
71 };
72
73 $.fn.hideFloaty = function(){
74 return this.each(function(){
75 var $el = $(this);
76 var oh = $el.get(0).offsetHeight;
77
78 $el.css('top', -oh-10).data('floatyVisible', false);
79 });
80 };
81
82 $.fn.toggleFloaty = function(){
83 return this.each(function(){
84 var $el = $(this);
85 if ($el.data('floatyVisible') === 'true'){
86 $el.hideFloaty();
87 }
88 else
89 {
90 $el.scrollFloaty();
91 }
92 });
93 };
94 });
95 }
96})($); \ No newline at end of file
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.location.js b/frontend/gamma/js/JQTouch/extensions/jqt.location.js
new file mode 100644
index 0000000..9d53a1a
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.location.js
@@ -0,0 +1,51 @@
1(function($) {
2 if ($.jQTouch)
3 {
4 $.jQTouch.addExtension(function Location(){
5
6 var latitude, longitude, callback, callback2;
7
8 function updateLocation(fn, fn2) {
9 if (navigator.geolocation)
10 {
11 callback = fn;
12 callback2 = fn2;
13 navigator.geolocation.getCurrentPosition(savePosition, failResponse);
14 return true;
15 } else {
16 console.log('Device not capable of geo-location.');
17 fn(false);
18 return false;
19 }
20 }
21 function failResponse(error){
22 if (callback2) {
23 callback2(error);
24 }
25 }
26 function savePosition(position) {
27 latitude = position.coords.latitude;
28 longitude = position.coords.longitude;
29 if (callback) {
30 callback(getLocation());
31 }
32 }
33 function getLocation() {
34 if (latitude && longitude) {
35 return {
36 latitude: latitude,
37 longitude: longitude
38 };
39
40 } else {
41 console.log('No location available. Try calling updateLocation() first.');
42 return false;
43 }
44 }
45 return {
46 updateLocation: updateLocation,
47 getLocation: getLocation
48 };
49 });
50 }
51})($); \ No newline at end of file
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.menusheet.js b/frontend/gamma/js/JQTouch/extensions/jqt.menusheet.js
new file mode 100644
index 0000000..8d21aca
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.menusheet.js
@@ -0,0 +1,137 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
12
13 (c) 2012 by jQTouch project members.
14 See LICENSE.txt for license.
15
16 Author: Thomas Yip
17*/
18
19(function($) {
20 var src = $("head script").last().attr("src") || '';
21 var scriptpath = src.split('?')[0].split('/').slice(0, -1).join('/')+'/';
22 var csspath = scriptpath + 'jqt.menusheet.css';
23 var link = $('<link href="' + csspath + '" rel="stylesheet">');
24 $('head').append($(link));
25
26 function hide(callback) {
27 var $target = $(this);
28 var data = $(this).data('menusheet');
29 if (data.shown) {
30 $(this).data('menusheet', {});
31 var $source = data.source;
32 $source.unbind('touchstart mousedown', data.closehandler);
33 $source.one('webkitTransitionEnd', function() {
34 $source.removeClass('inmotion transition in');
35 $target.removeClass('inmotion out');
36 !callback || callback.apply(this, arguments);
37 });
38
39 $source.addClass('inmotion transition in');
40 $target.addClass('inmotion out').removeClass('current');
41 $('#jqt').removeClass('menuopened');
42 }
43 return $target;
44 }
45
46 function show(callback) {
47 var $target = $(this);
48 var data = $(this).data('menusheet') || {};
49 if (!data.shown) {
50 var $source = $('#jqt .current:not(.menusheet)');
51 var closehandler = function() {
52 $target.menusheet('hide');
53 return false;
54 };
55
56 $source.one('webkitTransitionEnd', function() {
57 $source.one('touchstart mousedown', closehandler);
58 $source.removeClass('inmotion transition out');
59 $target.removeClass('inmotion in');
60 !callback || callback.apply(this, arguments);
61 });
62
63 data.shown = true;
64 data.closehandler = closehandler;
65 data.source = $source;
66 $(this).data('menusheet', data);
67
68 $source.addClass('inmotion transition out');
69 $target.addClass('current in');
70 $('#jqt').addClass('menuopened');
71 }
72 return $target;
73 }
74
75 var methods = {
76 init: function(options) {
77 $(this).addClass('menusheet');
78 $(this).data({shown: false});
79 },
80 show: show,
81 hide: hide
82 };
83
84 $.fn.menusheet = function(method) {
85 if (methods[method]) {
86 if ($(this).is('.menusheet')) {
87 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
88 } else {
89 var msg = 'Target is not a `menusheet`. Action `' + method + '` is ignored.';
90 console.warn(msg);
91 }
92 } else if ( typeof method === 'object' || ! method ) {
93 return methods.init.apply(this, arguments);
94 } else {
95 $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
96 }
97 };
98
99 if ($.jQTouch) {
100 $.jQTouch.addTapHandler({
101 name: 'open-menusheet',
102 isSupported: function(e, params) {
103 return params.$el.is('.menu');
104 },
105 fn: function(e, params) {
106 params.$el.removeClass('active');
107
108 var $target = $(params.hash);
109 $target.menusheet('show');
110
111 return false;
112 }
113 });
114 $.jQTouch.addTapHandler({
115 name: 'follow-menulink',
116 isSupported: function(e, params) {
117 if ($('#jqt').hasClass('menuopened')) {
118 return params.$el.is('.menusheet a');
119 }
120 return false;
121 },
122 fn: function(e, params) {
123 params.$el.removeClass('active');
124
125 var $target = params.$el.closest('.menusheet');
126 $target.menusheet('hide', function() {
127 if (!params.$el.is('.dismiss')) {
128 params.$el.trigger('tap');
129 }
130 });
131 return false;
132 }
133 });
134 } else {
135 console.error('Extension `jqt.menusheet` failed to load. jQT not found');
136 }
137})($);
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.offline.js b/frontend/gamma/js/JQTouch/extensions/jqt.offline.js
new file mode 100644
index 0000000..b333a16
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.offline.js
@@ -0,0 +1,97 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Created by David Kaneda <http://www.davidkaneda.com>
12 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
13
14 Special thanks to Jonathan Stark <http://jonathanstark.com/>
15
16 Lots of this code is specifically derived from Jonathan's book,
17 "Building iPhone Apps with HTML, CSS, and JavaScript"
18
19 (c) 2009 by jQTouch project members.
20 See LICENSE.txt for license.
21
22*/
23
24(function($) {
25 if ($.jQTouch)
26 {
27 $.jQTouch.addExtension(function Offline(){
28
29 // Convenience array of status values
30 var cacheStatusValues = [];
31 cacheStatusValues[0] = 'uncached';
32 cacheStatusValues[1] = 'idle';
33 cacheStatusValues[2] = 'checking';
34 cacheStatusValues[3] = 'downloading';
35 cacheStatusValues[4] = 'updateready';
36 cacheStatusValues[5] = 'obsolete';
37
38 // Listeners for all possible events
39 var cache = window.applicationCache;
40 cache.addEventListener('cached', logEvent, false);
41 cache.addEventListener('checking', logEvent, false);
42 cache.addEventListener('downloading', logEvent, false);
43 cache.addEventListener('error', logEvent, false);
44 cache.addEventListener('noupdate', logEvent, false);
45 cache.addEventListener('obsolete', logEvent, false);
46 cache.addEventListener('progress', logEvent, false);
47 cache.addEventListener('updateready', logEvent, false);
48
49 // Log every event to the console
50 function logEvent(e) {
51 var online, status, type, message;
52 online = (isOnline()) ? 'yes' : 'no';
53 status = cacheStatusValues[cache.status];
54 type = e.type;
55 message = 'online: ' + online;
56 message+= ', event: ' + type;
57 message+= ', status: ' + status;
58 if (type == 'error' && navigator.onLine) {
59 message+= ' There was an unknown error, check your Cache Manifest.';
60 }
61 console.log(message);
62 }
63
64 function isOnline() {
65 return navigator.onLine;
66 }
67
68 if (!$('html').attr('manifest')) {
69 console.log('No Cache Manifest listed on the <html> tag.')
70 }
71
72 // Swap in newly download files when update is ready
73 cache.addEventListener('updateready', function(e){
74 // Don't perform "swap" if this is the first cache
75 if (cacheStatusValues[cache.status] != 'idle') {
76 cache.swapCache();
77 console.log('Swapped/updated the Cache Manifest.');
78 }
79 }
80 , false);
81
82 // These two functions check for updates to the manifest file
83 function checkForUpdates(){
84 cache.update();
85 }
86 function autoCheckForUpdates(){
87 setInterval(function(){cache.update()}, 10000);
88 }
89
90 return {
91 isOnline: isOnline,
92 checkForUpdates: checkForUpdates,
93 autoCheckForUpdates: autoCheckForUpdates
94 }
95 });
96 }
97})(jQuery); \ No newline at end of file
diff --git a/frontend/gamma/js/JQTouch/extensions/jqt.themeswitcher.js b/frontend/gamma/js/JQTouch/extensions/jqt.themeswitcher.js
new file mode 100755
index 0000000..ef3a75d
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/extensions/jqt.themeswitcher.js
@@ -0,0 +1,123 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
12
13 (c) 2011 by jQTouch project members.
14 See LICENSE.txt for license.
15
16*/
17
18(function($) {
19 if ($.jQTouch) {
20
21 var scriptpath = $("script").last().attr("src").split('?')[0].split('/').slice(0, -1).join('/')+'/';
22
23 $.jQTouch.addExtension(function ThemeSwitcher(jQT) {
24
25 var current,
26 link,
27 titles = {},
28 defaults = {
29 themeStyleSelector: 'link[rel="stylesheet"][title]',
30 themeIncluded: [
31 {title: 'jQTouch', href: scriptpath + '../themes/css/jqtouch.css'},
32 {title: 'Apple', href: scriptpath + '../themes/css/apple.css'},
33 {title: 'Vanilla', href: scriptpath + '../themes/css/vanilla.css'}
34
35 ]
36 },
37 options = $.extend({}, defaults, jQT.settings);
38
39 function setStyleState(item, title) {
40 var $item = $(item);
41
42 if ($item.attr('title') === title) {
43 item.disabled = false; // workaround for Firefox on Zepto
44 $item.removeAttr('disabled');
45 } else {
46 item.disabled = true; // workaround for Firefox on Zepto
47 $item.attr('disabled', true);
48 }
49 }
50
51 function initializeStyleState(item, title) {
52 // and, workaround for WebKit by initializing the 'disabled' attribute
53 if (!current) {
54 current = title;
55 }
56 setStyleState(item, current);
57 }
58
59 // public
60 function switchStyle(title) {
61 current = title;
62 $(options.themeStyleSelector).each(function(i, item) {
63 setStyleState(item, title);
64 });
65 }
66
67 // collect title names, from <head>
68 $(options.themeStyleSelector).each(function(i, item) {
69 var $item = $(item);
70 var title = $item.attr('title');
71
72 titles[title] = true;
73
74 initializeStyleState(item, title);
75 });
76
77 // add included theme
78 for (var i=0; i < options.themeIncluded.length; i++) {
79 var hash = options.themeIncluded[i];
80 if (!(hash.title in titles)) {
81 link = $('<link title="' + hash.title + '" href="' + hash.href + '" rel="stylesheet">');
82 $('head').append($(link));
83
84 titles[hash.title] = true;
85
86 initializeStyleState(link, hash.title);
87 }
88 }
89
90 if (options.themeSelectionSelector) {
91 // create UI items
92 for (var title in titles) {
93 var $item = $('<li><a href="#" data-title="' + title + '">' + title + '</a></li>');
94 $(options.themeSelectionSelector).append($item);
95 }
96
97 // bind to UI items
98 $(options.themeSelectionSelector).delegate('* > a', 'tap', function(e) {
99 e.preventDefault();
100 e.stopPropagation();
101
102 var $a = $(this).closest('a');
103 $a.removeClass('active');
104 switchStyle($a.attr('data-title'));
105
106 // poor-man simulation of radio button behaviour
107 $(options.themeSelectionSelector).find('a').removeClass('selected');
108 $a.addClass('selected');
109 });
110
111 // poor-man simulation of radio button behaviour
112 $(options.themeSelectionSelector).closest('#jqt > *').bind('pageAnimationEnd', function(e, data){
113 if (data.direction === 'in') {
114 $(options.themeSelectionSelector).find('a[data-title="' + current + '"]').addClass('selected');
115 }
116 });
117 }
118
119 return {switchStyle: switchStyle};
120
121 });
122 }
123})($);
diff --git a/frontend/gamma/js/JQTouch/jqtouch.js b/frontend/gamma/js/JQTouch/jqtouch.js
new file mode 100644
index 0000000..bdc6d2e
--- a/dev/null
+++ b/frontend/gamma/js/JQTouch/jqtouch.js
@@ -0,0 +1,889 @@
1/*
2
3 _/ _/_/ _/_/_/_/_/ _/
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
8 _/
9 _/
10
11 Created by David Kaneda <http://www.davidkaneda.com>
12 Maintained by Thomas Yip <http://beedesk.com/>
13 Sponsored by Sencha Labs <http://www.sencha.com/>
14 Special thanks to Jonathan Stark <http://www.jonathanstark.com/>
15
16 Documentation and issue tracking on GitHub <http://github.com/senchalabs/jQTouch/>
17
18 (c) 2009-2011 Sencha Labs
19 jQTouch may be freely distributed under the MIT license.
20
21*/
22(function() {
23
24 var fx;
25 if ('Zepto' in window) {
26 fx = window.Zepto;
27 fx.fn.prop = fx.fn.attr;
28
29 Event.prototype.isDefaultPrevented = function() {
30 return this.defaultPrevented;
31 };
32 } else if ('jQuery' in window) {
33 fx = window.jQuery;
34
35 // trick to get Zepto/touch.js to work for jQuery
36 window.Zepto = $;
37 } else {
38 throw('Either Zepto or jQuery is required but neither can be found.');
39 }
40
41 $.jQTouch = function(options) {
42 // Initialize internal jQT variables
43 var $ = fx,
44 $body,
45 $head=$('head'),
46 history=[],
47 newPageCount=0,
48 jQTSettings={},
49 $currentPage='',
50 orientation='portrait',
51 touchSelectors=[],
52 publicObj={},
53 tapBuffer=100, // High click delay = ~350, quickest animation (slide) = 250
54 extensions=$.jQTouch.prototype.extensions,
55 extTapHandlers=$.jQTouch.prototype.tapHandlers,
56 tapHandlers=[],
57 animations=[],
58 hairExtensions='',
59 defaults = {
60 addGlossToIcon: true,
61 backSelector: '.back, .cancel, .goback',
62 cacheGetRequests: true,
63 debug: true,
64 defaultAnimation: 'slideleft',
65 fixedViewport: true,
66 formSelector: 'form',
67 fullScreen: true,
68 fullScreenClass: 'fullscreen',
69 icon: null,
70 icon4: null, // available in iOS 4.2 and later.
71 preloadImages: false,
72 startupScreen: null,
73 statusBar: 'default', // other options: black-translucent, black
74 submitSelector: '.submit',
75 touchSelector: 'a, .touch',
76 trackScrollPositions: true,
77 useAnimations: true,
78 useFastTouch: true,
79 useTouchScroll: true,
80 animations: [ // highest to lowest priority
81 {name:'cubeleft', selector:'.cubeleft, .cube', is3d: true},
82 {name:'cuberight', selector:'.cuberight', is3d: true},
83 {name:'dissolve', selector:'.dissolve'},
84 {name:'fade', selector:'.fade'},
85 {name:'flipleft', selector:'.flipleft, .flip', is3d: true},
86 {name:'flipright', selector:'.flipright', is3d: true},
87 {name:'pop', selector:'.pop', is3d: true},
88 {name:'swapleft', selector:'.swap', is3d: true},
89 {name:'slidedown', selector:'.slidedown'},
90 {name:'slideright', selector:'.slideright'},
91 {name:'slideup', selector:'.slideup'},
92 {name:'slideleft', selector:'.slideleft, .slide, #jqt > * > ul li a'}
93 ]
94 }; // end defaults
95
96 function warn(message) {
97 if (window.console !== undefined && jQTSettings.debug === true) {
98 console.warn(message);
99 }
100 }
101 function addAnimation(animation) {
102 if (typeof(animation.selector) === 'string' && typeof(animation.name) === 'string') {
103 animations.push(animation);
104 }
105 }
106 function addTapHandler(tapHandler) {
107 if (typeof(tapHandler.name) === 'string'
108 && typeof(tapHandler.isSupported) === 'function'
109 && typeof(tapHandler.fn) === 'function') {
110
111 tapHandlers.push(tapHandler);
112 }
113 }
114 function addPageToHistory(page, animation) {
115 history.unshift({
116 page: page,
117 animation: animation,
118 hash: '#' + page.attr('id'),
119 id: page.attr('id')
120 });
121 }
122
123 // Unfortunately, we can not assume the "tap" event
124 // is being used for links, forms, etc.
125 function clickHandler(e) {
126 // Figure out whether to prevent default
127 var $el = $(e.target);
128
129 // Find the nearest tappable ancestor
130 if (!$el.is(touchSelectors.join(', '))) {
131 $el = $(e.target).closest(touchSelectors.join(', '));
132 }
133
134 // Prevent default if we found an internal link (relative or absolute)
135 if ($el && $el.attr('href') && !$el.isExternalLink()) {
136 warn('Need to prevent default click behavior');
137 e.preventDefault();
138 } else {
139 warn('No need to prevent default click behavior');
140 }
141
142 // Trigger a tap event if touchstart is not on the job
143 if ($.support.touch) {
144 warn('Not converting click to a tap event because touch handler is on the job');
145 } else {
146 warn('Converting click event to a tap event because touch handlers are not present or off');
147 $(e.target).trigger('tap', e);
148 }
149
150 }
151 function doNavigation(fromPage, toPage, animation, goingBack) {
152
153 goingBack = goingBack ? goingBack : false;
154
155 // Error check for target page
156 if (toPage === undefined || toPage.length === 0) {
157 $.fn.unselect();
158 warn('Target element is missing.');
159 return false;
160 }
161
162 // Error check for fromPage===toPage
163 if (toPage.hasClass('current')) {
164 $.fn.unselect();
165 warn('You are already on the page you are trying to navigate to.');
166 return false;
167 }
168
169 // Collapse the keyboard
170 $(':focus').trigger('blur');
171
172 fromPage.trigger('pageAnimationStart', { direction: 'out', back: goingBack });
173 toPage.trigger('pageAnimationStart', { direction: 'in', back: goingBack });
174
175 if ($.support.animationEvents && animation && jQTSettings.useAnimations) {
176 // Fail over to 2d animation if need be
177 if (!$.support.transform3d && animation.is3d) {
178 warn('Did not detect support for 3d animations, falling back to ' + jQTSettings.defaultAnimation);
179 animation.name = jQTSettings.defaultAnimation;
180 }
181
182 // Reverse animation if need be
183 var finalAnimationName = animation.name,
184 is3d = animation.is3d ? 'animating3d' : '';
185
186 if (goingBack) {
187 finalAnimationName = finalAnimationName.replace(/left|right|up|down|in|out/, reverseAnimation );
188 }
189
190 warn('finalAnimationName is ' + finalAnimationName);
191
192 // Bind internal "cleanup" callback
193 fromPage.bind('webkitAnimationEnd', navigationEndHandler);
194
195 // Trigger animations
196 $body.addClass('animating ' + is3d);
197
198 var lastScroll = window.pageYOffset;
199
200 // Position the incoming page so toolbar is at top of viewport regardless of scroll position on from page
201 if (jQTSettings.trackScrollPositions === true) {
202 toPage.css('top', window.pageYOffset - (toPage.data('lastScroll') || 0));
203 }
204
205 toPage.addClass(finalAnimationName + ' in current');
206 fromPage.removeClass('current').addClass(finalAnimationName + ' out inmotion');
207
208 if (jQTSettings.trackScrollPositions === true) {
209 fromPage.data('lastScroll', lastScroll);
210 $('.scroll', fromPage).each(function(){
211 $(this).data('lastScroll', this.scrollTop);
212 });
213 }
214 } else {
215 toPage.addClass('current in');
216 fromPage.removeClass('current');
217 navigationEndHandler();
218 }
219
220 // Housekeeping
221 $currentPage = toPage;
222 if (goingBack) {
223 history.shift();
224 } else {
225 addPageToHistory($currentPage, animation);
226 }
227 setHash($currentPage.attr('id'));
228
229 // Private navigationEnd callback
230 function navigationEndHandler(event) {
231 var bufferTime = tapBuffer;
232
233 if ($.support.animationEvents && animation && jQTSettings.useAnimations) {
234 fromPage.unbind('webkitAnimationEnd', navigationEndHandler);
235 fromPage.removeClass(finalAnimationName + ' out inmotion');
236 if (finalAnimationName) {
237 toPage.removeClass(finalAnimationName);
238 }
239 $body.removeClass('animating animating3d');
240 if (jQTSettings.trackScrollPositions === true) {
241 toPage.css('top', -toPage.data('lastScroll'));
242
243 // Have to make sure the scroll/style resets
244 // are outside the flow of this function.
245 setTimeout(function(){
246 toPage.css('top', 0);
247 window.scroll(0, toPage.data('lastScroll'));
248 $('.scroll', toPage).each(function(){
249 this.scrollTop = - $(this).data('lastScroll');
250 });
251 }, 0);
252 }
253 } else {
254 fromPage.removeClass(finalAnimationName + ' out inmotion');
255 if (finalAnimationName) {
256 toPage.removeClass(finalAnimationName);
257 }
258 bufferTime += 260;
259 }
260
261 // In class is intentionally delayed, as it is our ghost click hack
262 setTimeout(function(){
263 toPage.removeClass('in');
264 window.scroll(0,0);
265 }, bufferTime);
266
267 fromPage.unselect();
268
269 // Trigger custom events
270 toPage.trigger('pageAnimationEnd', {
271 direction:'in', animation: animation, back: goingBack
272 });
273 fromPage.trigger('pageAnimationEnd', {
274 direction:'out', animation: animation, back: goingBack
275 });
276 }
277
278 return true;
279 }
280 function reverseAnimation(animation) {
281 var opposites={
282 'up' : 'down',
283 'down' : 'up',
284 'left' : 'right',
285 'right' : 'left',
286 'in' : 'out',
287 'out' : 'in'
288 };
289
290 return opposites[animation] || animation;
291 }
292 function getOrientation() {
293 return orientation;
294 }
295 function goBack() {
296
297 // Error checking
298 if (history.length < 1 ) {
299 warn('History is empty.');
300 }
301
302 if (history.length === 1 ) {
303 warn('You are on the first panel.');
304 window.history.go(-1);
305 }
306
307 var from = history[0],
308 to = history[1];
309
310 if (doNavigation(from.page, to.page, from.animation, true)) {
311 return publicObj;
312 } else {
313 warn('Could not go back.');
314 return false;
315 }
316
317 }
318 function goTo(toPage, animation) {
319
320 var fromPage = history[0].page;
321
322 if (typeof animation === 'string') {
323 for (var i=0, max=animations.length; i < max; i++) {
324 if (animations[i].name === animation) {
325 animation = animations[i];
326 break;
327 }
328 }
329 }
330
331 if (typeof toPage === 'string') {
332 var nextPage = $(toPage);
333
334 if (nextPage.length < 1) {
335 showPageByHref(toPage, {
336 animation: animation
337 });
338 return;
339 } else {
340 toPage = nextPage;
341 }
342 }
343 if (doNavigation(fromPage, toPage, animation)) {
344 return publicObj;
345 } else {
346 warn('Could not animate pages.');
347 return false;
348 }
349 }
350 function hashChangeHandler(e) {
351 if (location.hash === history[0].hash) {
352 warn('We are on the right panel');
353 return true;
354 } else if (location.hash === '') {
355 goBack();
356 return true;
357 } else {
358 if( (history[1] && location.hash === history[1].hash) ) {
359 goBack();
360 return true;
361 } else {
362 // Lastly, just try going to the ID...
363 warn('Could not find ID in history, just forwarding to DOM element.');
364 goTo($(location.hash), jQTSettings.defaultAnimation);
365 }
366 }
367 }
368 function init(options) {
369 jQTSettings = $.extend({}, defaults, options);
370
371 // Preload images
372 if (jQTSettings.preloadImages) {
373 for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--) {
374 (new Image()).src = jQTSettings.preloadImages[i];
375 }
376 }
377
378 // Set appropriate icon (retina display available in iOS 4.2 and later.)
379 var precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed';
380 if (jQTSettings.icon) {
381 hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" href="' + jQTSettings.icon + '" />';
382 }
383 if (jQTSettings.icon4) {
384 hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" sizes="114x114" href="' + jQTSettings.icon4 + '" />';
385 }
386 // Set startup screen
387 if (jQTSettings.startupScreen) {
388 hairExtensions += '<link rel="apple-touch-startup-image" href="' + jQTSettings.startupScreen + '" />';
389 }
390
391 // Set viewport
392 if (jQTSettings.fixedViewport) {
393 hairExtensions += '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>';
394 }
395
396 // Set full-screen
397 if (jQTSettings.fullScreen) {
398 hairExtensions += '<meta name="apple-mobile-web-app-capable" content="yes" />';
399 if (jQTSettings.statusBar) {
400 hairExtensions += '<meta name="apple-mobile-web-app-status-bar-style" content="' + jQTSettings.statusBar + '" />';
401 }
402 }
403
404 // Attach hair extensions
405 if (hairExtensions) {
406 $head.prepend(hairExtensions);
407 }
408 }
409
410 function getAnimation(el) {
411 var animation;
412
413 for (var i=0, max=animations.length; i < max; i++) {
414 if (el.is(animations[i].selector)) {
415 animation = animations[i];
416 break;
417 }
418 }
419
420 if (!animation) {
421 warn('Animation could not be found. Using ' + jQTSettings.defaultAnimation + '.');
422 animation = jQTSettings.defaultAnimation;
423 }
424 return animation;
425 }
426
427 function insertPages(nodes, animation) {
428
429 var targetPage = null;
430
431 // Call dom.createElement element directly instead of relying on $(nodes),
432 // to work around: https://github.com/madrobby/zepto/issues/312
433 var div = document.createElement('div');
434 div.innerHTML = nodes;
435
436 $(div).children().each(function(index, node) {
437 var $node = $(this);
438 if (!$node.attr('id')) {
439 $node.attr('id', 'page-' + (++newPageCount));
440 }
441
442 // Remove any existing instance
443 $('#' + $node.attr('id')).remove();
444
445 $body.append($node);
446 $body.trigger('pageInserted', {page: $node});
447
448 if ($node.hasClass('current') || !targetPage) {
449 targetPage = $node;
450 }
451 });
452 if (targetPage !== null) {
453 goTo(targetPage, animation);
454 return targetPage;
455 } else {
456 return false;
457 }
458 }
459
460 function orientationChangeHandler() {
461 $body.css('minHeight', 1000);
462 scrollTo(0,0);
463 var bodyHeight = window.innerHeight;
464 $body.css('minHeight', bodyHeight);
465
466 orientation = Math.abs(window.orientation) == 90 ? 'landscape' : 'portrait';
467 $body.removeClass('portrait landscape').addClass(orientation).trigger('turn', {orientation: orientation});
468 }
469 function setHash(hash) {
470 // Sanitize
471 location.hash = '#' + hash.replace(/^#/, '');
472 }
473 function showPageByHref(href, options) {
474
475 var defaults = {
476 data: null,
477 method: 'GET',
478 animation: null,
479 callback: null,
480 $referrer: null
481 };
482
483 var settings = $.extend({}, defaults, options);
484
485 if (href != '#') {
486 $.ajax({
487 url: href,
488 data: settings.data,
489 type: settings.method,
490 success: function (data) {
491 var firstPage = insertPages(data, settings.animation);
492 if (firstPage) {
493 if (settings.method == 'GET' && jQTSettings.cacheGetRequests === true && settings.$referrer) {
494 settings.$referrer.attr('href', '#' + firstPage.attr('id'));
495 }
496 if (settings.callback) {
497 settings.callback(true);
498 }
499 }
500 },
501 error: function (data) {
502 if (settings.$referrer) {
503 settings.$referrer.unselect();
504 }
505 if (settings.callback) {
506 settings.callback(false);
507 }
508 }
509 });
510 } else if (settings.$referrer) {
511 settings.$referrer.unselect();
512 }
513 }
514 function submitHandler(e, callback) {
515
516 $(':focus').trigger('blur');
517
518 e.preventDefault();
519
520 var $form = (typeof(e)==='string') ? $(e).eq(0) : (e.target ? $(e.target) : $(e));
521
522 if ($form.length && $form.is(jQTSettings.formSelector) && $form.attr('action')) {
523 showPageByHref($form.attr('action'), {
524 data: $form.serialize(),
525 method: $form.attr('method') || "POST",
526 animation: getAnimation($form),
527 callback: callback
528 });
529 return false;
530 }
531 return true;
532 }
533 function submitParentForm($el) {
534
535 var $form = $el.closest('form');
536 if ($form.length === 0) {
537 warn('No parent form found');
538 } else {
539 warn('About to submit parent form');
540 $form.trigger('submit');
541 return false;
542 }
543 return true;
544 }
545 function supportForTransform3d() {
546
547 var head, body, style, div, result;
548
549 head = document.getElementsByTagName('head')[0];
550 body = document.body;
551
552 style = document.createElement('style');
553 style.textContent = '@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-webkit-transform-3d){#jqt-3dtest{height:3px}}';
554
555 div = document.createElement('div');
556 div.id = 'jqt-3dtest';
557
558 // Add to the page
559 head.appendChild(style);
560 body.appendChild(div);
561
562 // Check the result
563 result = div.offsetHeight === 3;
564
565 // Clean up
566 style.parentNode.removeChild(style);
567 div.parentNode.removeChild(div);
568
569 // Pass back result
570 warn('Support for 3d transforms: ' + result);
571 return result;
572 }
573 function supportIOS5() {
574 var support = false;
575 var REGEX_IOS_VERSION = /OS (\d+)(_\d+)* like Mac OS X/i;
576
577 var agentString = window.navigator.userAgent;
578 if (REGEX_IOS_VERSION.test(agentString)) {
579 support = (REGEX_IOS_VERSION.exec(agentString)[1] >= 5);
580 }
581 return support;
582 }
583 function touchStartHandler(e){
584
585 var $el = $(e.target),
586 selectors = touchSelectors.join(', ');
587
588 // Find the nearest tappable ancestor
589 if (!$el.is(selectors)) {
590 $el = $el.closest(selectors);
591 }
592
593 // Make sure we have a tappable element
594 if ($el.length && $el.attr('href')) {
595 $el.addClass('active');
596 }
597
598 // Remove our active class if we move
599 $el.on($.support.touch ? 'touchmove' : 'mousemove', function(){
600 $el.removeClass('active');
601 });
602
603 $el.on('touchend', function(){
604 $el.unbind('touchmove mousemove');
605 });
606
607 }
608 function tapHandler(e){
609
610 if (e.isDefaultPrevented()) {
611 return true;
612 }
613
614 // Grab the target element
615 var $el = $(e.target);
616
617 // Find the nearest tappable ancestor
618 if (!$el.is(touchSelectors.join(', '))) {
619 $el = $el.closest(touchSelectors.join(', '));
620 }
621
622 // Make sure we have a tappable element
623 if (!$el.length || !$el.attr('href')) {
624 warn('Could not find a link related to tapped element');
625 return true;
626 }
627
628 // Init some vars
629 var target = $el.attr('target'),
630 hash = $el.prop('hash'),
631 href = $el.attr('href');
632
633 var params = {
634 e: e,
635 $el: $el,
636 target: target,
637 hash: hash,
638 href: href,
639 jQTSettings: jQTSettings
640 };
641
642 // Loop thru all handlers
643 for (var i=0, len=tapHandlers.length; i<len; i++) {
644 var handler = tapHandlers[i];
645 var supported = handler.isSupported(e, params);
646 if (supported) {
647 var flag = handler.fn(e, params);
648 return flag;
649 }
650 }
651 }
652 function addDefaultTapHandlers() {
653 addTapHandler({
654 name: 'external-link',
655 isSupported: function(e, params) {
656 return params.$el.isExternalLink();
657 },
658 fn: function(e, params) {
659 params.$el.unselect();
660 return true;
661 }
662 });
663 addTapHandler({
664 name: 'back-selector',
665 isSupported: function(e, params) {
666 return params.$el.is(params.jQTSettings.backSelector);
667 },
668 fn: function(e, params) {
669 // User clicked or tapped a back button
670 goBack(params.hash);
671 }
672 });
673 addTapHandler({
674 name: 'submit-selector',
675 isSupported: function(e, params) {
676 return params.$el.is(params.jQTSettings.submitSelector);
677 },
678 fn: function(e, params) {
679 // User clicked or tapped a submit element
680 submitParentForm(params.$el);
681 }
682 });
683 addTapHandler({
684 name: 'webapp',
685 isSupported: function(e, params) {
686 return params.target === '_webapp';
687 },
688 fn: function(e, params) {
689 // User clicked or tapped an internal link, fullscreen mode
690 window.location = params.href;
691 return false;
692 }
693 });
694 addTapHandler({
695 name: 'no-op',
696 isSupported: function(e, params) {
697 return params.href === '#';
698 },
699 fn: function(e, params) {
700 // Allow tap on item with no href
701 params.$el.unselect();
702 return true;
703 }
704 });
705 addTapHandler({
706 name: 'standard',
707 isSupported: function(e, params) {
708 return params.hash && params.hash !== '#';
709 },
710 fn: function(e, params) {
711 var animation = getAnimation(params.$el);
712 // Internal href
713 params.$el.addClass('active');
714 goTo(
715 $(params.hash).data('referrer', params.$el),
716 animation,
717 params.$el.hasClass('reverse')
718 );
719 return false;
720 }
721 });
722 addTapHandler({
723 name: 'external',
724 isSupported: function(e, params) {
725 return true;
726 },
727 fn: function(e, params) {
728 var animation = getAnimation(params.$el);
729
730 // External href
731 params.$el.addClass('loading active');
732 showPageByHref(params.$el.attr('href'), {
733 animation: animation,
734 callback: function() {
735 params.$el.removeClass('loading');
736 setTimeout($.fn.unselect, 250, params.$el);
737 },
738 $referrer: params.$el
739 });
740 return false;
741 }
742 });
743 };
744
745 // Get the party started
746 init(options);
747
748 // Document ready stuff
749 $(document).ready(function RUMBLE() {
750 // Store some properties in a support object
751 if (!$.support) $.support = {};
752 $.support.animationEvents = (typeof window.WebKitAnimationEvent != 'undefined');
753 $.support.touch = (typeof window.TouchEvent != 'undefined') && (window.navigator.userAgent.indexOf('Mobile') > -1) && jQTSettings.useFastTouch;
754 $.support.transform3d = supportForTransform3d();
755 $.support.ios5 = supportIOS5();
756
757 if (!$.support.touch) {
758 warn('This device does not support touch interaction, or it has been deactivated by the developer. Some features might be unavailable.');
759 }
760 if (!$.support.transform3d) {
761 warn('This device does not support 3d animation. 2d animations will be used instead.');
762 }
763
764 // Define public jQuery functions
765 $.fn.isExternalLink = function() {
766 var $el = $(this);
767 return ($el.attr('target') == '_blank' || $el.attr('rel') == 'external' || $el.is('a[href^="http://maps.google.com"], a[href^="mailto:"], a[href^="tel:"], a[href^="javascript:"], a[href*="youtube.com/v"], a[href*="youtube.com/watch"]'));
768 };
769 $.fn.makeActive = function() {
770 return $(this).addClass('active');
771 };
772 $.fn.unselect = function(obj) {
773 if (obj) {
774 obj.removeClass('active');
775 } else {
776 $('.active').removeClass('active');
777 }
778 };
779
780 // Add extensions
781 for (var i=0, max=extensions.length; i < max; i++) {
782 var fn = extensions[i];
783 if ($.isFunction(fn)) {
784 $.extend(publicObj, fn(publicObj));
785 }
786 }
787
788 // Add extensions tapHandlers
789 for (var i=0, max=extTapHandlers.length; i < max; i++) {
790 addTapHandler(extTapHandlers[i]);
791 }
792 // Add default tapHandlers
793 addDefaultTapHandlers();
794
795 // Add animations
796 for (var j=0, max_anims=defaults.animations.length; j < max_anims; j++) {
797 var animation = defaults.animations[j];
798 if(jQTSettings[animation.name + 'Selector'] !== undefined){
799 animation.selector = jQTSettings[animation.name + 'Selector'];
800 }
801 addAnimation(animation);
802 }
803
804 // Create an array of stuff that needs touch event handling
805 touchSelectors.push(jQTSettings.touchSelector);
806 touchSelectors.push(jQTSettings.backSelector);
807 touchSelectors.push(jQTSettings.submitSelector);
808 $(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none');
809
810 // Make sure we have a jqt element
811 $body = $('#jqt');
812 var anatomy_lessons = [];
813
814 if ($body.length === 0) {
815 warn('Could not find an element with the id "jqt", so the body id has been set to "jqt". If you are having any problems, wrapping your panels in a div with the id "jqt" might help.');
816 $body = $(document.body).attr('id', 'jqt');
817 }
818
819 // Add some specific css if need be
820 if ($.support.transform3d) {
821 anatomy_lessons.push('supports3d');
822 }
823 if ($.support.ios5 && jQTSettings.useTouchScroll) {
824 anatomy_lessons.push('touchscroll');
825 }
826
827 if (jQTSettings.fullScreenClass && window.navigator.standalone === true) {
828 anatomy_lessons.push(jQTSettings.fullScreenClass, jQTSettings.statusBar);
829 }
830
831 // Bind events
832
833 $body
834 .addClass(anatomy_lessons.join(' '))
835 .bind('click', clickHandler)
836 .bind('orientationchange', orientationChangeHandler)
837 .bind('submit', submitHandler)
838 .bind('tap', tapHandler)
839 .bind( $.support.touch ? 'touchstart' : 'mousedown', touchStartHandler)
840 .trigger('orientationchange');
841
842 $(window).bind('hashchange', hashChangeHandler);
843
844 var startHash = location.hash;
845
846 // Determine what the initial view should be
847 if ($('#jqt > .current').length === 0) {
848 $currentPage = $('#jqt > *:first-child').addClass('current');
849 } else {
850 $currentPage = $('#jqt > .current');
851 }
852
853 setHash($currentPage.attr('id'));
854 addPageToHistory($currentPage);
855
856 if ($(startHash).length === 1) {
857 goTo(startHash);
858 }
859 });
860
861 // Expose public methods and properties
862 publicObj = {
863 addAnimation: addAnimation,
864 animations: animations,
865 getOrientation: getOrientation,
866 goBack: goBack,
867 insertPages: insertPages,
868 goTo: goTo,
869 history: history,
870 settings: jQTSettings,
871 submitForm: submitHandler
872 };
873 return publicObj;
874 };
875
876 $.jQTouch.prototype.extensions = [];
877 $.jQTouch.prototype.tapHandlers = [];
878
879 // Extensions directly manipulate the jQTouch object, before it's initialized.
880 $.jQTouch.addExtension = function(extension) {
881 $.jQTouch.prototype.extensions.push(extension);
882 };
883
884 // Experimental tap hanlders that can bypass default jQTouch tap handling
885 $.jQTouch.addTapHandler = function(extension) {
886 $.jQTouch.prototype.tapHandlers.push(extension);
887 };
888
889})(); // Double closure, ALL THE WAY ACROSS THE SKY
diff --git a/frontend/gamma/js/Zepto/ajax.js b/frontend/gamma/js/Zepto/ajax.js
new file mode 100644
index 0000000..f4da150
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/ajax.js
@@ -0,0 +1,285 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 var jsonpID = 0,
7 isObject = $.isObject,
8 document = window.document,
9 key,
10 name,
11 rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
12 scriptTypeRE = /^(?:text|application)\/javascript/i,
13 xmlTypeRE = /^(?:text|application)\/xml/i,
14 jsonType = 'application/json',
15 htmlType = 'text/html',
16 blankRE = /^\s*$/
17
18 // trigger a custom event and return false if it was cancelled
19 function triggerAndReturn(context, eventName, data) {
20 var event = $.Event(eventName)
21 $(context).trigger(event, data)
22 return !event.defaultPrevented
23 }
24
25 // trigger an Ajax "global" event
26 function triggerGlobal(settings, context, eventName, data) {
27 if (settings.global) return triggerAndReturn(context || document, eventName, data)
28 }
29
30 // Number of active Ajax requests
31 $.active = 0
32
33 function ajaxStart(settings) {
34 if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
35 }
36 function ajaxStop(settings) {
37 if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
38 }
39
40 // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
41 function ajaxBeforeSend(xhr, settings) {
42 var context = settings.context
43 if (settings.beforeSend.call(context, xhr, settings) === false ||
44 triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
45 return false
46
47 triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
48 }
49 function ajaxSuccess(data, xhr, settings) {
50 var context = settings.context, status = 'success'
51 settings.success.call(context, data, status, xhr)
52 triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
53 ajaxComplete(status, xhr, settings)
54 }
55 // type: "timeout", "error", "abort", "parsererror"
56 function ajaxError(error, type, xhr, settings) {
57 var context = settings.context
58 settings.error.call(context, xhr, type, error)
59 triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error])
60 ajaxComplete(type, xhr, settings)
61 }
62 // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
63 function ajaxComplete(status, xhr, settings) {
64 var context = settings.context
65 settings.complete.call(context, xhr, status)
66 triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
67 ajaxStop(settings)
68 }
69
70 // Empty function, used as default callback
71 function empty() {}
72
73 $.ajaxJSONP = function(options){
74 if (!('type' in options)) return $.ajax(options)
75
76 var callbackName = 'jsonp' + (++jsonpID),
77 script = document.createElement('script'),
78 abort = function(){
79 $(script).remove()
80 if (callbackName in window) window[callbackName] = empty
81 ajaxComplete('abort', xhr, options)
82 },
83 xhr = { abort: abort }, abortTimeout
84
85 if (options.error) script.onerror = function() {
86 xhr.abort()
87 options.error()
88 }
89
90 window[callbackName] = function(data){
91 clearTimeout(abortTimeout)
92 $(script).remove()
93 delete window[callbackName]
94 ajaxSuccess(data, xhr, options)
95 }
96
97 serializeData(options)
98 script.src = options.url.replace(/=\?/, '=' + callbackName)
99 $('head').append(script)
100
101 if (options.timeout > 0) abortTimeout = setTimeout(function(){
102 xhr.abort()
103 ajaxComplete('timeout', xhr, options)
104 }, options.timeout)
105
106 return xhr
107 }
108
109 $.ajaxSettings = {
110 // Default type of request
111 type: 'GET',
112 // Callback that is executed before request
113 beforeSend: empty,
114 // Callback that is executed if the request succeeds
115 success: empty,
116 // Callback that is executed the the server drops error
117 error: empty,
118 // Callback that is executed on request complete (both: error and success)
119 complete: empty,
120 // The context for the callbacks
121 context: null,
122 // Whether to trigger "global" Ajax events
123 global: true,
124 // Transport
125 xhr: function () {
126 return new window.XMLHttpRequest()
127 },
128 // MIME types mapping
129 accepts: {
130 script: 'text/javascript, application/javascript',
131 json: jsonType,
132 xml: 'application/xml, text/xml',
133 html: htmlType,
134 text: 'text/plain'
135 },
136 // Whether the request is to another domain
137 crossDomain: false,
138 // Default timeout
139 timeout: 0,
140 // Whether data should be serialized to string
141 processData: true
142 }
143
144 function mimeToDataType(mime) {
145 return mime && ( mime == htmlType ? 'html' :
146 mime == jsonType ? 'json' :
147 scriptTypeRE.test(mime) ? 'script' :
148 xmlTypeRE.test(mime) && 'xml' ) || 'text'
149 }
150
151 function appendQuery(url, query) {
152 return (url + '&' + query).replace(/[&?]{1,2}/, '?')
153 }
154
155 // serialize payload and append it to the URL for GET requests
156 function serializeData(options) {
157 if (options.processData && isObject(options.data))
158 options.data = $.param(options.data, options.traditional)
159 if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
160 options.url = appendQuery(options.url, options.data)
161 }
162
163 $.ajax = function(options){
164 var settings = $.extend({}, options || {})
165 for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
166
167 ajaxStart(settings)
168
169 if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
170 RegExp.$2 != window.location.host
171
172 var dataType = settings.dataType, hasPlaceholder = /=\?/.test(settings.url)
173 if (dataType == 'jsonp' || hasPlaceholder) {
174 if (!hasPlaceholder) settings.url = appendQuery(settings.url, 'callback=?')
175 return $.ajaxJSONP(settings)
176 }
177
178 if (!settings.url) settings.url = window.location.toString()
179 serializeData(settings)
180
181 var mime = settings.accepts[dataType],
182 baseHeaders = { },
183 protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
184 xhr = $.ajaxSettings.xhr(), abortTimeout
185
186 if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest'
187 if (mime) {
188 baseHeaders['Accept'] = mime
189 if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
190 xhr.overrideMimeType && xhr.overrideMimeType(mime)
191 }
192 if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
193 baseHeaders['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded')
194 settings.headers = $.extend(baseHeaders, settings.headers || {})
195
196 xhr.onreadystatechange = function(){
197 if (xhr.readyState == 4) {
198 xhr.onreadystatechange = empty;
199 clearTimeout(abortTimeout)
200 var result, error = false
201 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
202 dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type'))
203 result = xhr.responseText
204
205 try {
206 if (dataType == 'script') (1,eval)(result)
207 else if (dataType == 'xml') result = xhr.responseXML
208 else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
209 } catch (e) { error = e }
210
211 if (error) ajaxError(error, 'parsererror', xhr, settings)
212 else ajaxSuccess(result, xhr, settings)
213 } else {
214 ajaxError(null, xhr.status ? 'error' : 'abort', xhr, settings)
215 }
216 }
217 }
218
219 var async = 'async' in settings ? settings.async : true
220 xhr.open(settings.type, settings.url, async)
221
222 for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name])
223
224 if (ajaxBeforeSend(xhr, settings) === false) {
225 xhr.abort()
226 return false
227 }
228
229 if (settings.timeout > 0) abortTimeout = setTimeout(function(){
230 xhr.onreadystatechange = empty
231 xhr.abort()
232 ajaxError(null, 'timeout', xhr, settings)
233 }, settings.timeout)
234
235 // avoid sending empty string (#319)
236 xhr.send(settings.data ? settings.data : null)
237 return xhr
238 }
239
240 $.get = function(url, success){ return $.ajax({ url: url, success: success }) }
241
242 $.post = function(url, data, success, dataType){
243 if ($.isFunction(data)) dataType = dataType || success, success = data, data = null
244 return $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType })
245 }
246
247 $.getJSON = function(url, success){
248 return $.ajax({ url: url, success: success, dataType: 'json' })
249 }
250
251 $.fn.load = function(url, success){
252 if (!this.length) return this
253 var self = this, parts = url.split(/\s/), selector
254 if (parts.length > 1) url = parts[0], selector = parts[1]
255 $.get(url, function(response){
256 self.html(selector ?
257 $('<div>').html(response.replace(rscript, "")).find(selector)
258 : response)
259 success && success.apply(self, arguments)
260 })
261 return this
262 }
263
264 var escape = encodeURIComponent
265
266 function serialize(params, obj, traditional, scope){
267 var array = $.isArray(obj)
268 $.each(obj, function(key, value) {
269 if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']'
270 // handle data in serializeArray() format
271 if (!scope && array) params.add(value.name, value.value)
272 // recurse into nested objects
273 else if (traditional ? $.isArray(value) : isObject(value))
274 serialize(params, value, traditional, key)
275 else params.add(key, value)
276 })
277 }
278
279 $.param = function(obj, traditional){
280 var params = []
281 params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }
282 serialize(params, obj, traditional)
283 return params.join('&').replace(/%20/g, '+')
284 }
285})(Zepto)
diff --git a/frontend/gamma/js/Zepto/assets.js b/frontend/gamma/js/Zepto/assets.js
new file mode 100644
index 0000000..b5a5712
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/assets.js
@@ -0,0 +1,21 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 var cache = [], timeout
7
8 $.fn.remove = function(){
9 return this.each(function(){
10 if(this.parentNode){
11 if(this.tagName === 'IMG'){
12 cache.push(this)
13 this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
14 if (timeout) clearTimeout(timeout)
15 timeout = setTimeout(function(){ cache = [] }, 60000)
16 }
17 this.parentNode.removeChild(this)
18 }
19 })
20 }
21})(Zepto)
diff --git a/frontend/gamma/js/Zepto/data.js b/frontend/gamma/js/Zepto/data.js
new file mode 100644
index 0000000..b4c289f
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/data.js
@@ -0,0 +1,67 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5// The following code is heavily inspired by jQuery's $.fn.data()
6
7;(function($) {
8 var data = {}, dataAttr = $.fn.data, camelize = $.camelCase,
9 exp = $.expando = 'Zepto' + (+new Date())
10
11 // Get value from node:
12 // 1. first try key as given,
13 // 2. then try camelized key,
14 // 3. fall back to reading "data-*" attribute.
15 function getData(node, name) {
16 var id = node[exp], store = id && data[id]
17 if (name === undefined) return store || setData(node)
18 else {
19 if (store) {
20 if (name in store) return store[name]
21 var camelName = camelize(name)
22 if (camelName in store) return store[camelName]
23 }
24 return dataAttr.call($(node), name)
25 }
26 }
27
28 // Store value under camelized key on node
29 function setData(node, name, value) {
30 var id = node[exp] || (node[exp] = ++$.uuid),
31 store = data[id] || (data[id] = attributeData(node))
32 if (name !== undefined) store[camelize(name)] = value
33 return store
34 }
35
36 // Read all "data-*" attributes from a node
37 function attributeData(node) {
38 var store = {}
39 $.each(node.attributes, function(i, attr){
40 if (attr.name.indexOf('data-') == 0)
41 store[camelize(attr.name.replace('data-', ''))] =
42 $.zepto.deserializeValue(attr.value)
43 })
44 return store
45 }
46
47 $.fn.data = function(name, value) {
48 return value === undefined ?
49 // set multiple values via object
50 $.isPlainObject(name) ?
51 this.each(function(i, node){
52 $.each(name, function(key, value){ setData(node, key, value) })
53 }) :
54 // get value from first element
55 this.length == 0 ? undefined : getData(this[0], name) :
56 // set value on all elements
57 this.each(function(){ setData(this, name, value) })
58 }
59
60 $.fn.removeData = function(names) {
61 if (typeof names == 'string') names = names.split(/\s+/)
62 return this.each(function(){
63 var id = this[exp], store = id && data[id]
64 if (store) $.each(names, function(){ delete store[camelize(this)] })
65 })
66 }
67})(Zepto)
diff --git a/frontend/gamma/js/Zepto/detect.js b/frontend/gamma/js/Zepto/detect.js
new file mode 100644
index 0000000..22c0386
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/detect.js
@@ -0,0 +1,42 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 function detect(ua){
7 var os = this.os = {}, browser = this.browser = {},
8 webkit = ua.match(/WebKit\/([\d.]+)/),
9 android = ua.match(/(Android)\s+([\d.]+)/),
10 ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
11 iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
12 webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
13 touchpad = webos && ua.match(/TouchPad/),
14 kindle = ua.match(/Kindle\/([\d.]+)/),
15 silk = ua.match(/Silk\/([\d._]+)/),
16 blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/),
17 chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/)
18
19 // todo clean this up with a better OS/browser
20 // separation. we need to discern between multiple
21 // browsers on android, and decide if kindle fire in
22 // silk mode is android or not
23
24 if (browser.webkit = !!webkit) browser.version = webkit[1]
25
26 if (android) os.android = true, os.version = android[2]
27 if (iphone) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.')
28 if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.')
29 if (webos) os.webos = true, os.version = webos[2]
30 if (touchpad) os.touchpad = true
31 if (blackberry) os.blackberry = true, os.version = blackberry[2]
32 if (kindle) os.kindle = true, os.version = kindle[1]
33 if (silk) browser.silk = true, browser.version = silk[1]
34 if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true
35 if (chrome) browser.chrome = true, browser.version = chrome[1]
36 }
37
38 detect.call($, navigator.userAgent)
39 // make available to unit tests
40 $.__detect = detect
41
42})(Zepto)
diff --git a/frontend/gamma/js/Zepto/event.js b/frontend/gamma/js/Zepto/event.js
new file mode 100644
index 0000000..b40af22
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/event.js
@@ -0,0 +1,248 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={},
7 hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
8
9 specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
10
11 function zid(element) {
12 return element._zid || (element._zid = _zid++)
13 }
14 function findHandlers(element, event, fn, selector) {
15 event = parse(event)
16 if (event.ns) var matcher = matcherFor(event.ns)
17 return (handlers[zid(element)] || []).filter(function(handler) {
18 return handler
19 && (!event.e || handler.e == event.e)
20 && (!event.ns || matcher.test(handler.ns))
21 && (!fn || zid(handler.fn) === zid(fn))
22 && (!selector || handler.sel == selector)
23 })
24 }
25 function parse(event) {
26 var parts = ('' + event).split('.')
27 return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
28 }
29 function matcherFor(ns) {
30 return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
31 }
32
33 function eachEvent(events, fn, iterator){
34 if ($.isObject(events)) $.each(events, iterator)
35 else events.split(/\s/).forEach(function(type){ iterator(type, fn) })
36 }
37
38 function eventCapture(handler, captureSetting) {
39 return handler.del &&
40 (handler.e == 'focus' || handler.e == 'blur') ||
41 !!captureSetting
42 }
43
44 function realEvent(type) {
45 return hover[type] || type
46 }
47
48 function add(element, events, fn, selector, getDelegate, capture){
49 var id = zid(element), set = (handlers[id] || (handlers[id] = []))
50 eachEvent(events, fn, function(event, fn){
51 var handler = parse(event)
52 handler.fn = fn
53 handler.sel = selector
54 // emulate mouseenter, mouseleave
55 if (handler.e in hover) fn = function(e){
56 var related = e.relatedTarget
57 if (!related || (related !== this && !$.contains(this, related)))
58 return handler.fn.apply(this, arguments)
59 }
60 handler.del = getDelegate && getDelegate(fn, event)
61 var callback = handler.del || fn
62 handler.proxy = function (e) {
63 var result = callback.apply(element, [e].concat(e.data))
64 if (result === false) e.preventDefault(), e.stopPropagation()
65 return result
66 }
67 handler.i = set.length
68 set.push(handler)
69 element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
70 })
71 }
72 function remove(element, events, fn, selector, capture){
73 var id = zid(element)
74 eachEvent(events || '', fn, function(event, fn){
75 findHandlers(element, event, fn, selector).forEach(function(handler){
76 delete handlers[id][handler.i]
77 element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
78 })
79 })
80 }
81
82 $.event = { add: add, remove: remove }
83
84 $.proxy = function(fn, context) {
85 if ($.isFunction(fn)) {
86 var proxyFn = function(){ return fn.apply(context, arguments) }
87 proxyFn._zid = zid(fn)
88 return proxyFn
89 } else if (typeof context == 'string') {
90 return $.proxy(fn[context], fn)
91 } else {
92 throw new TypeError("expected function")
93 }
94 }
95
96 $.fn.bind = function(event, callback){
97 return this.each(function(){
98 add(this, event, callback)
99 })
100 }
101 $.fn.unbind = function(event, callback){
102 return this.each(function(){
103 remove(this, event, callback)
104 })
105 }
106 $.fn.one = function(event, callback){
107 return this.each(function(i, element){
108 add(this, event, callback, null, function(fn, type){
109 return function(){
110 var result = fn.apply(element, arguments)
111 remove(element, type, fn)
112 return result
113 }
114 })
115 })
116 }
117
118 var returnTrue = function(){return true},
119 returnFalse = function(){return false},
120 ignoreProperties = /^([A-Z]|layer[XY]$)/,
121 eventMethods = {
122 preventDefault: 'isDefaultPrevented',
123 stopImmediatePropagation: 'isImmediatePropagationStopped',
124 stopPropagation: 'isPropagationStopped'
125 }
126 function createProxy(event) {
127 var key, proxy = { originalEvent: event }
128 for (key in event)
129 if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
130
131 $.each(eventMethods, function(name, predicate) {
132 proxy[name] = function(){
133 this[predicate] = returnTrue
134 return event[name].apply(event, arguments)
135 }
136 proxy[predicate] = returnFalse
137 })
138 return proxy
139 }
140
141 // emulates the 'defaultPrevented' property for browsers that have none
142 function fix(event) {
143 if (!('defaultPrevented' in event)) {
144 event.defaultPrevented = false
145 var prevent = event.preventDefault
146 event.preventDefault = function() {
147 this.defaultPrevented = true
148 prevent.call(this)
149 }
150 }
151 }
152
153 $.fn.delegate = function(selector, event, callback){
154 return this.each(function(i, element){
155 add(element, event, callback, selector, function(fn){
156 return function(e){
157 var evt, match = $(e.target).closest(selector, element).get(0)
158 if (match) {
159 evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
160 return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
161 }
162 }
163 })
164 })
165 }
166 $.fn.undelegate = function(selector, event, callback){
167 return this.each(function(){
168 remove(this, event, callback, selector)
169 })
170 }
171
172 $.fn.live = function(event, callback){
173 $(document.body).delegate(this.selector, event, callback)
174 return this
175 }
176 $.fn.die = function(event, callback){
177 $(document.body).undelegate(this.selector, event, callback)
178 return this
179 }
180
181 $.fn.on = function(event, selector, callback){
182 return !selector || $.isFunction(selector) ?
183 this.bind(event, selector || callback) : this.delegate(selector, event, callback)
184 }
185 $.fn.off = function(event, selector, callback){
186 return !selector || $.isFunction(selector) ?
187 this.unbind(event, selector || callback) : this.undelegate(selector, event, callback)
188 }
189
190 $.fn.trigger = function(event, data){
191 if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
192 fix(event)
193 event.data = data
194 return this.each(function(){
195 // items in the collection might not be DOM elements
196 // (todo: possibly support events on plain old objects)
197 if('dispatchEvent' in this) this.dispatchEvent(event)
198 })
199 }
200
201 // triggers event handlers on current element just as if an event occurred,
202 // doesn't trigger an actual event, doesn't bubble
203 $.fn.triggerHandler = function(event, data){
204 var e, result
205 this.each(function(i, element){
206 e = createProxy(typeof event == 'string' ? $.Event(event) : event)
207 e.data = data
208 e.target = element
209 $.each(findHandlers(element, event.type || event), function(i, handler){
210 result = handler.proxy(e)
211 if (e.isImmediatePropagationStopped()) return false
212 })
213 })
214 return result
215 }
216
217 // shortcut methods for `.bind(event, fn)` for each event type
218 ;('focusin focusout load resize scroll unload click dblclick '+
219 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
220 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
221 $.fn[event] = function(callback) {
222 return callback ?
223 this.bind(event, callback) :
224 this.trigger(event)
225 }
226 })
227
228 ;['focus', 'blur'].forEach(function(name) {
229 $.fn[name] = function(callback) {
230 if (callback) this.bind(name, callback)
231 else this.each(function(){
232 try { this[name]() }
233 catch(e) {}
234 })
235 return this
236 }
237 })
238
239 $.Event = function(type, props) {
240 if (typeof type != 'string') props = type, type = props.type
241 var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
242 if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
243 event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
244 event.isDefaultPrevented = function(){ return this.defaultPrevented }
245 return event
246 }
247
248})(Zepto)
diff --git a/frontend/gamma/js/Zepto/form.js b/frontend/gamma/js/Zepto/form.js
new file mode 100644
index 0000000..4d562a6
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/form.js
@@ -0,0 +1,40 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function ($) {
6 $.fn.serializeArray = function () {
7 var result = [], el
8 $( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
9 el = $(this)
10 var type = el.attr('type')
11 if (this.nodeName.toLowerCase() != 'fieldset' &&
12 !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
13 ((type != 'radio' && type != 'checkbox') || this.checked))
14 result.push({
15 name: el.attr('name'),
16 value: el.val()
17 })
18 })
19 return result
20 }
21
22 $.fn.serialize = function () {
23 var result = []
24 this.serializeArray().forEach(function (elm) {
25 result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) )
26 })
27 return result.join('&')
28 }
29
30 $.fn.submit = function (callback) {
31 if (callback) this.bind('submit', callback)
32 else if (this.length) {
33 var event = $.Event('submit')
34 this.eq(0).trigger(event)
35 if (!event.defaultPrevented) this.get(0).submit()
36 }
37 return this
38 }
39
40})(Zepto)
diff --git a/frontend/gamma/js/Zepto/fx.js b/frontend/gamma/js/Zepto/fx.js
new file mode 100644
index 0000000..575449a
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/fx.js
@@ -0,0 +1,102 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($, undefined){
6 var prefix = '', eventPrefix, endEventName, endAnimationName,
7 vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
8 document = window.document, testEl = document.createElement('div'),
9 supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
10 transform,
11 transitionProperty, transitionDuration, transitionTiming,
12 animationName, animationDuration, animationTiming,
13 cssReset = {}
14
15 function dasherize(str) { return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2')) }
16 function downcase(str) { return str.toLowerCase() }
17 function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }
18
19 $.each(vendors, function(vendor, event){
20 if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
21 prefix = '-' + downcase(vendor) + '-'
22 eventPrefix = event
23 return false
24 }
25 })
26
27 transform = prefix + 'transform'
28 cssReset[transitionProperty = prefix + 'transition-property'] =
29 cssReset[transitionDuration = prefix + 'transition-duration'] =
30 cssReset[transitionTiming = prefix + 'transition-timing-function'] =
31 cssReset[animationName = prefix + 'animation-name'] =
32 cssReset[animationDuration = prefix + 'animation-duration'] =
33 cssReset[animationTiming = prefix + 'animation-timing-function'] = ''
34
35 $.fx = {
36 off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
37 speeds: { _default: 400, fast: 200, slow: 600 },
38 cssPrefix: prefix,
39 transitionEnd: normalizeEvent('TransitionEnd'),
40 animationEnd: normalizeEvent('AnimationEnd')
41 }
42
43 $.fn.animate = function(properties, duration, ease, callback){
44 if ($.isObject(duration))
45 ease = duration.easing, callback = duration.complete, duration = duration.duration
46 if (duration) duration = (typeof duration == 'number' ? duration :
47 ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
48 return this.anim(properties, duration, ease, callback)
49 }
50
51 $.fn.anim = function(properties, duration, ease, callback){
52 var key, cssValues = {}, cssProperties, transforms = '',
53 that = this, wrappedCallback, endEvent = $.fx.transitionEnd
54
55 if (duration === undefined) duration = 0.4
56 if ($.fx.off) duration = 0
57
58 if (typeof properties == 'string') {
59 // keyframe animation
60 cssValues[animationName] = properties
61 cssValues[animationDuration] = duration + 's'
62 cssValues[animationTiming] = (ease || 'linear')
63 endEvent = $.fx.animationEnd
64 } else {
65 cssProperties = []
66 // CSS transitions
67 for (key in properties)
68 if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
69 else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
70
71 if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
72 if (duration > 0 && typeof properties === 'object') {
73 cssValues[transitionProperty] = cssProperties.join(', ')
74 cssValues[transitionDuration] = duration + 's'
75 cssValues[transitionTiming] = (ease || 'linear')
76 }
77 }
78
79 wrappedCallback = function(event){
80 if (typeof event !== 'undefined') {
81 if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
82 $(event.target).unbind(endEvent, arguments.callee)
83 }
84 $(this).css(cssReset)
85 callback && callback.call(this)
86 }
87 if (duration > 0) this.bind(endEvent, wrappedCallback)
88
89 // trigger page reflow so new elements can animate
90 this.size() && this.get(0).clientLeft
91
92 this.css(cssValues)
93
94 if (duration <= 0) setTimeout(function() {
95 that.each(function(){ wrappedCallback.call(this) })
96 }, 0)
97
98 return this
99 }
100
101 testEl = null
102})(Zepto)
diff --git a/frontend/gamma/js/Zepto/fx_methods.js b/frontend/gamma/js/Zepto/fx_methods.js
new file mode 100644
index 0000000..23daf6e
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/fx_methods.js
@@ -0,0 +1,71 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($, undefined){
6 var document = window.document, docElem = document.documentElement,
7 origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle
8
9 function anim(el, speed, opacity, scale, callback) {
10 if (typeof speed == 'function' && !callback) callback = speed, speed = undefined
11 var props = { opacity: opacity }
12 if (scale) {
13 props.scale = scale
14 el.css($.fx.cssPrefix + 'transform-origin', '0 0')
15 }
16 return el.animate(props, speed, null, callback)
17 }
18
19 function hide(el, speed, scale, callback) {
20 return anim(el, speed, 0, scale, function(){
21 origHide.call($(this))
22 callback && callback.call(this)
23 })
24 }
25
26 $.fn.show = function(speed, callback) {
27 origShow.call(this)
28 if (speed === undefined) speed = 0
29 else this.css('opacity', 0)
30 return anim(this, speed, 1, '1,1', callback)
31 }
32
33 $.fn.hide = function(speed, callback) {
34 if (speed === undefined) return origHide.call(this)
35 else return hide(this, speed, '0,0', callback)
36 }
37
38 $.fn.toggle = function(speed, callback) {
39 if (speed === undefined || typeof speed == 'boolean')
40 return origToggle.call(this, speed)
41 else return this.each(function(){
42 var el = $(this)
43 el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback)
44 })
45 }
46
47 $.fn.fadeTo = function(speed, opacity, callback) {
48 return anim(this, speed, opacity, null, callback)
49 }
50
51 $.fn.fadeIn = function(speed, callback) {
52 var target = this.css('opacity')
53 if (target > 0) this.css('opacity', 0)
54 else target = 1
55 return origShow.call(this).fadeTo(speed, target, callback)
56 }
57
58 $.fn.fadeOut = function(speed, callback) {
59 return hide(this, speed, null, callback)
60 }
61
62 $.fn.fadeToggle = function(speed, callback) {
63 return this.each(function(){
64 var el = $(this)
65 el[
66 (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut'
67 ](speed, callback)
68 })
69 }
70
71})(Zepto)
diff --git a/frontend/gamma/js/Zepto/gesture.js b/frontend/gamma/js/Zepto/gesture.js
new file mode 100644
index 0000000..035455b
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/gesture.js
@@ -0,0 +1,35 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 if ($.os.ios) {
7 var gesture = {}, gestureTimeout
8
9 function parentIfText(node){
10 return 'tagName' in node ? node : node.parentNode
11 }
12
13 $(document).bind('gesturestart', function(e){
14 var now = Date.now(), delta = now - (gesture.last || now)
15 gesture.target = parentIfText(e.target)
16 gestureTimeout && clearTimeout(gestureTimeout)
17 gesture.e1 = e.scale
18 gesture.last = now
19 }).bind('gesturechange', function(e){
20 gesture.e2 = e.scale
21 }).bind('gestureend', function(e){
22 if (gesture.e2 > 0) {
23 Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') &&
24 $(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out'))
25 gesture.e1 = gesture.e2 = gesture.last = 0
26 } else if ('last' in gesture) {
27 gesture = {}
28 }
29 })
30
31 ;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){
32 $.fn[m] = function(callback){ return this.bind(m, callback) }
33 })
34 }
35})(Zepto)
diff --git a/frontend/gamma/js/Zepto/polyfill.js b/frontend/gamma/js/Zepto/polyfill.js
new file mode 100644
index 0000000..933d1f8
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/polyfill.js
@@ -0,0 +1,36 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function(undefined){
6 if (String.prototype.trim === undefined) // fix for iOS 3.2
7 String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') }
8
9 // For iOS 3.x
10 // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
11 if (Array.prototype.reduce === undefined)
12 Array.prototype.reduce = function(fun){
13 if(this === void 0 || this === null) throw new TypeError()
14 var t = Object(this), len = t.length >>> 0, k = 0, accumulator
15 if(typeof fun != 'function') throw new TypeError()
16 if(len == 0 && arguments.length == 1) throw new TypeError()
17
18 if(arguments.length >= 2)
19 accumulator = arguments[1]
20 else
21 do{
22 if(k in t){
23 accumulator = t[k++]
24 break
25 }
26 if(++k >= len) throw new TypeError()
27 } while (true)
28
29 while (k < len){
30 if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
31 k++
32 }
33 return accumulator
34 }
35
36})()
diff --git a/frontend/gamma/js/Zepto/selector.js b/frontend/gamma/js/Zepto/selector.js
new file mode 100644
index 0000000..c0b035a
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/selector.js
@@ -0,0 +1,81 @@
1;(function($){
2 var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches
3
4 function visible(elem){
5 elem = $(elem)
6 return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
7 }
8
9 // Implements a subset from:
10 // http://api.jquery.com/category/selectors/jquery-selector-extensions/
11 //
12 // Each filter function receives the current index, all nodes in the
13 // considered set, and a value if there were parentheses. The value
14 // of `this` is the node currently being considered. The function returns the
15 // resulting node(s), null, or undefined.
16 //
17 // Complex selectors are not supported:
18 // li:has(label:contains("foo")) + li:has(label:contains("bar"))
19 // ul.inner:first > li
20 var filters = $.expr[':'] = {
21 visible: function(){ if (visible(this)) return this },
22 hidden: function(){ if (!visible(this)) return this },
23 selected: function(){ if (this.selected) return this },
24 checked: function(){ if (this.checked) return this },
25 parent: function(){ return this.parentNode },
26 first: function(idx){ if (idx === 0) return this },
27 last: function(idx, nodes){ if (idx === nodes.length - 1) return this },
28 eq: function(idx, _, value){ if (idx === value) return this },
29 contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this },
30 has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this }
31 }
32
33 var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'),
34 childRe = /^\s*>/,
35 classTag = 'Zepto' + (+new Date())
36
37 function process(sel, fn) {
38 // quote the hash in `a[href^=#]` expression
39 sel = sel.replace(/=#\]/g, '="#"]')
40 var filter, arg, match = filterRe.exec(sel)
41 if (match && match[2] in filters) {
42 var filter = filters[match[2]], arg = match[3]
43 sel = match[1]
44 if (arg) {
45 var num = Number(arg)
46 if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
47 else arg = num
48 }
49 }
50 return fn(sel, filter, arg)
51 }
52
53 zepto.qsa = function(node, selector) {
54 return process(selector, function(sel, filter, arg){
55 try {
56 var taggedParent
57 if (!sel && filter) sel = '*'
58 else if (childRe.test(sel))
59 // support "> *" child queries by tagging the parent node with a
60 // unique class and prepending that classname onto the selector
61 taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel
62
63 var nodes = oldQsa(node, sel)
64 } catch(e) {
65 console.error('error performing selector: %o', selector)
66 throw e
67 } finally {
68 if (taggedParent) taggedParent.removeClass(classTag)
69 }
70 return !filter ? nodes :
71 zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
72 })
73 }
74
75 zepto.matches = function(node, selector){
76 return process(selector, function(sel, filter, arg){
77 return (!sel || oldMatches(node, sel)) &&
78 (!filter || filter.call(node, null, arg) === node)
79 })
80 }
81})(Zepto)
diff --git a/frontend/gamma/js/Zepto/stack.js b/frontend/gamma/js/Zepto/stack.js
new file mode 100644
index 0000000..c995285
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/stack.js
@@ -0,0 +1,22 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 $.fn.end = function(){
7 return this.prevObject || $()
8 }
9
10 $.fn.andSelf = function(){
11 return this.add(this.prevObject || $())
12 }
13
14 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
15 var fn = $.fn[property]
16 $.fn[property] = function(){
17 var ret = fn.apply(this, arguments)
18 ret.prevObject = this
19 return ret
20 }
21 })
22})(Zepto)
diff --git a/frontend/gamma/js/Zepto/touch.js b/frontend/gamma/js/Zepto/touch.js
new file mode 100644
index 0000000..af109b9
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/touch.js
@@ -0,0 +1,113 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5;(function($){
6 var touch = {},
7 touchTimeout, tapTimeout, swipeTimeout,
8 longTapDelay = 750, longTapTimeout
9
10 function parentIfText(node) {
11 return 'tagName' in node ? node : node.parentNode
12 }
13
14 function swipeDirection(x1, x2, y1, y2) {
15 var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2)
16 return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
17 }
18
19 function longTap() {
20 longTapTimeout = null
21 if (touch.last) {
22 touch.el.trigger('longTap')
23 touch = {}
24 }
25 }
26
27 function cancelLongTap() {
28 if (longTapTimeout) clearTimeout(longTapTimeout)
29 longTapTimeout = null
30 }
31
32 function cancelAll() {
33 if (touchTimeout) clearTimeout(touchTimeout)
34 if (tapTimeout) clearTimeout(tapTimeout)
35 if (swipeTimeout) clearTimeout(swipeTimeout)
36 if (longTapTimeout) clearTimeout(longTapTimeout)
37 touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
38 touch = {}
39 }
40
41 $(document).ready(function(){
42 var now, delta
43
44 $(document.body)
45 .bind('touchstart', function(e){
46 now = Date.now()
47 delta = now - (touch.last || now)
48 touch.el = $(parentIfText(e.touches[0].target))
49 touchTimeout && clearTimeout(touchTimeout)
50 touch.x1 = e.touches[0].pageX
51 touch.y1 = e.touches[0].pageY
52 if (delta > 0 && delta <= 250) touch.isDoubleTap = true
53 touch.last = now
54 longTapTimeout = setTimeout(longTap, longTapDelay)
55 })
56 .bind('touchmove', function(e){
57 cancelLongTap()
58 touch.x2 = e.touches[0].pageX
59 touch.y2 = e.touches[0].pageY
60 })
61 .bind('touchend', function(e){
62 cancelLongTap()
63
64 // swipe
65 if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
66 (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
67
68 swipeTimeout = setTimeout(function() {
69 touch.el.trigger('swipe')
70 touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
71 touch = {}
72 }, 0)
73
74 // normal tap
75 else if ('last' in touch)
76
77 // delay by one tick so we can cancel the 'tap' event if 'scroll' fires
78 // ('tap' fires before 'scroll')
79 tapTimeout = setTimeout(function() {
80
81 // trigger universal 'tap' with the option to cancelTouch()
82 // (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
83 var event = $.Event('tap')
84 event.cancelTouch = cancelAll
85 touch.el.trigger(event)
86
87 // trigger double tap immediately
88 if (touch.isDoubleTap) {
89 touch.el.trigger('doubleTap')
90 touch = {}
91 }
92
93 // trigger single tap after 250ms of inactivity
94 else {
95 touchTimeout = setTimeout(function(){
96 touchTimeout = null
97 touch.el.trigger('singleTap')
98 touch = {}
99 }, 250)
100 }
101
102 }, 0)
103
104 })
105 .bind('touchcancel', cancelAll)
106
107 $(window).bind('scroll', cancelAll)
108 })
109
110 ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
111 $.fn[m] = function(callback){ return this.bind(m, callback) }
112 })
113})(Zepto)
diff --git a/frontend/gamma/js/Zepto/zepto.js b/frontend/gamma/js/Zepto/zepto.js
new file mode 100644
index 0000000..e67b3a2
--- a/dev/null
+++ b/frontend/gamma/js/Zepto/zepto.js
@@ -0,0 +1,751 @@
1// Zepto.js
2// (c) 2010-2012 Thomas Fuchs
3// Zepto.js may be freely distributed under the MIT license.
4
5var Zepto = (function() {
6 var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
7 document = window.document,
8 elementDisplay = {}, classCache = {},
9 getComputedStyle = document.defaultView.getComputedStyle,
10 cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
11 fragmentRE = /^\s*<(\w+|!)[^>]*>/,
12 tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
13 rootNodeRE = /^(?:body|html)$/i,
14
15 // special attributes that should be get/set via method calls
16 methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
17
18 adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
19 table = document.createElement('table'),
20 tableRow = document.createElement('tr'),
21 containers = {
22 'tr': document.createElement('tbody'),
23 'tbody': table, 'thead': table, 'tfoot': table,
24 'td': tableRow, 'th': tableRow,
25 '*': document.createElement('div')
26 },
27 readyRE = /complete|loaded|interactive/,
28 classSelectorRE = /^\.([\w-]+)$/,
29 idSelectorRE = /^#([\w-]*)$/,
30 tagSelectorRE = /^[\w-]+$/,
31 toString = {}.toString,
32 zepto = {},
33 camelize, uniq,
34 tempParent = document.createElement('div')
35
36 zepto.matches = function(element, selector) {
37 if (!element || element.nodeType !== 1) return false
38 var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
39 element.oMatchesSelector || element.matchesSelector
40 if (matchesSelector) return matchesSelector.call(element, selector)
41 // fall back to performing a selector:
42 var match, parent = element.parentNode, temp = !parent
43 if (temp) (parent = tempParent).appendChild(element)
44 match = ~zepto.qsa(parent, selector).indexOf(element)
45 temp && tempParent.removeChild(element)
46 return match
47 }
48
49 function isFunction(value) { return toString.call(value) == "[object Function]" }
50 function isObject(value) { return value instanceof Object }
51 function isPlainObject(value) {
52 return isObject(value) && value != window && value.__proto__ == Object.prototype
53 }
54 function isArray(value) { return value instanceof Array }
55 function likeArray(obj) { return typeof obj.length == 'number' }
56
57 function compact(array) { return filter.call(array, function(item){ return item != null }) }
58 function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
59 camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
60 function dasherize(str) {
61 return str.replace(/::/g, '/')
62 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
63 .replace(/([a-z\d])([A-Z])/g, '$1_$2')
64 .replace(/_/g, '-')
65 .toLowerCase()
66 }
67 uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
68
69 function classRE(name) {
70 return name in classCache ?
71 classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
72 }
73
74 function maybeAddPx(name, value) {
75 return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
76 }
77
78 function defaultDisplay(nodeName) {
79 var element, display
80 if (!elementDisplay[nodeName]) {
81 element = document.createElement(nodeName)
82 document.body.appendChild(element)
83 display = getComputedStyle(element, '').getPropertyValue("display")
84 element.parentNode.removeChild(element)
85 display == "none" && (display = "block")
86 elementDisplay[nodeName] = display
87 }
88 return elementDisplay[nodeName]
89 }
90
91 function children(element) {
92 return 'children' in element ?
93 slice.call(element.children) :
94 $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
95 }
96
97 // `$.zepto.fragment` takes a html string and an optional tag name
98 // to generate DOM nodes nodes from the given html string.
99 // The generated DOM nodes are returned as an array.
100 // This function can be overriden in plugins for example to make
101 // it compatible with browsers that don't support the DOM fully.
102 zepto.fragment = function(html, name, properties) {
103 if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
104 if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
105 if (!(name in containers)) name = '*'
106
107 var nodes, dom, container = containers[name]
108 container.innerHTML = '' + html
109 dom = $.each(slice.call(container.childNodes), function(){
110 container.removeChild(this)
111 })
112 if (isPlainObject(properties)) {
113 nodes = $(dom)
114 $.each(properties, function(key, value) {
115 if (methodAttributes.indexOf(key) > -1) nodes[key](value)
116 else nodes.attr(key, value)
117 })
118 }
119 return dom
120 }
121
122 // `$.zepto.Z` swaps out the prototype of the given `dom` array
123 // of nodes with `$.fn` and thus supplying all the Zepto functions
124 // to the array. Note that `__proto__` is not supported on Internet
125 // Explorer. This method can be overriden in plugins.
126 zepto.Z = function(dom, selector) {
127 dom = dom || []
128 dom.__proto__ = arguments.callee.prototype
129 dom.selector = selector || ''
130 return dom
131 }
132
133 // `$.zepto.isZ` should return `true` if the given object is a Zepto
134 // collection. This method can be overriden in plugins.
135 zepto.isZ = function(object) {
136 return object instanceof zepto.Z
137 }
138
139 // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
140 // takes a CSS selector and an optional context (and handles various
141 // special cases).
142 // This method can be overriden in plugins.
143 zepto.init = function(selector, context) {
144 // If nothing given, return an empty Zepto collection
145 if (!selector) return zepto.Z()
146 // If a function is given, call it when the DOM is ready
147 else if (isFunction(selector)) return $(document).ready(selector)
148 // If a Zepto collection is given, juts return it
149 else if (zepto.isZ(selector)) return selector
150 else {
151 var dom
152 // normalize array if an array of nodes is given
153 if (isArray(selector)) dom = compact(selector)
154 // Wrap DOM nodes. If a plain object is given, duplicate it.
155 else if (isObject(selector))
156 dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
157 // If it's a html fragment, create nodes from it
158 else if (fragmentRE.test(selector))
159 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
160 // If there's a context, create a collection on that context first, and select
161 // nodes from there
162 else if (context !== undefined) return $(context).find(selector)
163 // And last but no least, if it's a CSS selector, use it to select nodes.
164 else dom = zepto.qsa(document, selector)
165 // create a new Zepto collection from the nodes found
166 return zepto.Z(dom, selector)
167 }
168 }
169
170 // `$` will be the base `Zepto` object. When calling this
171 // function just call `$.zepto.init, which makes the implementation
172 // details of selecting nodes and creating Zepto collections
173 // patchable in plugins.
174 $ = function(selector, context){
175 return zepto.init(selector, context)
176 }
177
178 function extend(target, source, deep) {
179 for (key in source)
180 if (deep && isPlainObject(source[key])) {
181 if (!isPlainObject(target[key])) target[key] = {}
182 extend(target[key], source[key], deep)
183 }
184 else if (source[key] !== undefined) target[key] = source[key]
185 }
186
187 // Copy all but undefined properties from one or more
188 // objects to the `target` object.
189 $.extend = function(target){
190 var deep, args = slice.call(arguments, 1)
191 if (typeof target == 'boolean') {
192 deep = target
193 target = args.shift()
194 }
195 args.forEach(function(arg){ extend(target, arg, deep) })
196 return target
197 }
198
199 // `$.zepto.qsa` is Zepto's CSS selector implementation which
200 // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
201 // This method can be overriden in plugins.
202 zepto.qsa = function(element, selector){
203 var found
204 return (element === document && idSelectorRE.test(selector)) ?
205 ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) :
206 (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
207 slice.call(
208 classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
209 tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
210 element.querySelectorAll(selector)
211 )
212 }
213
214 function filtered(nodes, selector) {
215 return selector === undefined ? $(nodes) : $(nodes).filter(selector)
216 }
217
218 $.contains = function(parent, node) {
219 return parent !== node && parent.contains(node)
220 }
221
222 function funcArg(context, arg, idx, payload) {
223 return isFunction(arg) ? arg.call(context, idx, payload) : arg
224 }
225
226 function setAttribute(node, name, value) {
227 value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
228 }
229
230 // access className property while respecting SVGAnimatedString
231 function className(node, value){
232 var klass = node.className,
233 svg = klass && klass.baseVal !== undefined
234
235 if (value === undefined) return svg ? klass.baseVal : klass
236 svg ? (klass.baseVal = value) : (node.className = value)
237 }
238
239 // "true" => true
240 // "false" => false
241 // "null" => null
242 // "42" => 42
243 // "42.5" => 42.5
244 // JSON => parse if valid
245 // String => self
246 function deserializeValue(value) {
247 var num
248 try {
249 return value ?
250 value == "true" ||
251 ( value == "false" ? false :
252 value == "null" ? null :
253 !isNaN(num = Number(value)) ? num :
254 /^[\[\{]/.test(value) ? $.parseJSON(value) :
255 value )
256 : value
257 } catch(e) {
258 return value
259 }
260 }
261
262 $.isFunction = isFunction
263 $.isObject = isObject
264 $.isArray = isArray
265 $.isPlainObject = isPlainObject
266
267 $.inArray = function(elem, array, i){
268 return emptyArray.indexOf.call(array, elem, i)
269 }
270
271 $.camelCase = camelize
272 $.trim = function(str) { return str.trim() }
273
274 // plugin compatibility
275 $.uuid = 0
276 $.support = { }
277 $.expr = { }
278
279 $.map = function(elements, callback){
280 var value, values = [], i, key
281 if (likeArray(elements))
282 for (i = 0; i < elements.length; i++) {
283 value = callback(elements[i], i)
284 if (value != null) values.push(value)
285 }
286 else
287 for (key in elements) {
288 value = callback(elements[key], key)
289 if (value != null) values.push(value)
290 }
291 return flatten(values)
292 }
293
294 $.each = function(elements, callback){
295 var i, key
296 if (likeArray(elements)) {
297 for (i = 0; i < elements.length; i++)
298 if (callback.call(elements[i], i, elements[i]) === false) return elements
299 } else {
300 for (key in elements)
301 if (callback.call(elements[key], key, elements[key]) === false) return elements
302 }
303
304 return elements
305 }
306
307 $.grep = function(elements, callback){
308 return filter.call(elements, callback)
309 }
310
311 if (window.JSON) $.parseJSON = JSON.parse
312
313 // Define methods that will be available on all
314 // Zepto collections
315 $.fn = {
316 // Because a collection acts like an array
317 // copy over these useful array functions.
318 forEach: emptyArray.forEach,
319 reduce: emptyArray.reduce,
320 push: emptyArray.push,
321 sort: emptyArray.sort,
322 indexOf: emptyArray.indexOf,
323 concat: emptyArray.concat,
324
325 // `map` and `slice` in the jQuery API work differently
326 // from their array counterparts
327 map: function(fn){
328 return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
329 },
330 slice: function(){
331 return $(slice.apply(this, arguments))
332 },
333
334 ready: function(callback){
335 if (readyRE.test(document.readyState)) callback($)
336 else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
337 return this
338 },
339 get: function(idx){
340 return idx === undefined ? slice.call(this) : this[idx]
341 },
342 toArray: function(){ return this.get() },
343 size: function(){
344 return this.length
345 },
346 remove: function(){
347 return this.each(function(){
348 if (this.parentNode != null)
349 this.parentNode.removeChild(this)
350 })
351 },
352 each: function(callback){
353 emptyArray.every.call(this, function(el, idx){
354 return callback.call(el, idx, el) !== false
355 })
356 return this
357 },
358 filter: function(selector){
359 if (isFunction(selector)) return this.not(this.not(selector))
360 return $(filter.call(this, function(element){
361 return zepto.matches(element, selector)
362 }))
363 },
364 add: function(selector,context){
365 return $(uniq(this.concat($(selector,context))))
366 },
367 is: function(selector){
368 return this.length > 0 && zepto.matches(this[0], selector)
369 },
370 not: function(selector){
371 var nodes=[]
372 if (isFunction(selector) && selector.call !== undefined)
373 this.each(function(idx){
374 if (!selector.call(this,idx)) nodes.push(this)
375 })
376 else {
377 var excludes = typeof selector == 'string' ? this.filter(selector) :
378 (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
379 this.forEach(function(el){
380 if (excludes.indexOf(el) < 0) nodes.push(el)
381 })
382 }
383 return $(nodes)
384 },
385 has: function(selector){
386 return this.filter(function(){
387 return isObject(selector) ?
388 $.contains(this, selector) :
389 $(this).find(selector).size()
390 })
391 },
392 eq: function(idx){
393 return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
394 },
395 first: function(){
396 var el = this[0]
397 return el && !isObject(el) ? el : $(el)
398 },
399 last: function(){
400 var el = this[this.length - 1]
401 return el && !isObject(el) ? el : $(el)
402 },
403 find: function(selector){
404 var result
405 if (this.length == 1) result = $(zepto.qsa(this[0], selector))
406 else result = this.map(function(){ return zepto.qsa(this, selector) })
407 return result
408 },
409 closest: function(selector, context){
410 var node = this[0]
411 while (node && !zepto.matches(node, selector))
412 node = node !== context && node !== document && node.parentNode
413 return $(node)
414 },
415 parents: function(selector){
416 var ancestors = [], nodes = this
417 while (nodes.length > 0)
418 nodes = $.map(nodes, function(node){
419 if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
420 ancestors.push(node)
421 return node
422 }
423 })
424 return filtered(ancestors, selector)
425 },
426 parent: function(selector){
427 return filtered(uniq(this.pluck('parentNode')), selector)
428 },
429 children: function(selector){
430 return filtered(this.map(function(){ return children(this) }), selector)
431 },
432 contents: function() {
433 return this.map(function() { return slice.call(this.childNodes) })
434 },
435 siblings: function(selector){
436 return filtered(this.map(function(i, el){
437 return filter.call(children(el.parentNode), function(child){ return child!==el })
438 }), selector)
439 },
440 empty: function(){
441 return this.each(function(){ this.innerHTML = '' })
442 },
443 // `pluck` is borrowed from Prototype.js
444 pluck: function(property){
445 return $.map(this, function(el){ return el[property] })
446 },
447 show: function(){
448 return this.each(function(){
449 this.style.display == "none" && (this.style.display = null)
450 if (getComputedStyle(this, '').getPropertyValue("display") == "none")
451 this.style.display = defaultDisplay(this.nodeName)
452 })
453 },
454 replaceWith: function(newContent){
455 return this.before(newContent).remove()
456 },
457 wrap: function(structure){
458 var func = isFunction(structure)
459 if (this[0] && !func)
460 var dom = $(structure).get(0),
461 clone = dom.parentNode || this.length > 1
462
463 return this.each(function(index){
464 $(this).wrapAll(
465 func ? structure.call(this, index) :
466 clone ? dom.cloneNode(true) : dom
467 )
468 })
469 },
470 wrapAll: function(structure){
471 if (this[0]) {
472 $(this[0]).before(structure = $(structure))
473 var children
474 // drill down to the inmost element
475 while ((children = structure.children()).length) structure = children.first()
476 $(structure).append(this)
477 }
478 return this
479 },
480 wrapInner: function(structure){
481 var func = isFunction(structure)
482 return this.each(function(index){
483 var self = $(this), contents = self.contents(),
484 dom = func ? structure.call(this, index) : structure
485 contents.length ? contents.wrapAll(dom) : self.append(dom)
486 })
487 },
488 unwrap: function(){
489 this.parent().each(function(){
490 $(this).replaceWith($(this).children())
491 })
492 return this
493 },
494 clone: function(){
495 return this.map(function(){ return this.cloneNode(true) })
496 },
497 hide: function(){
498 return this.css("display", "none")
499 },
500 toggle: function(setting){
501 return this.each(function(){
502 var el = $(this)
503 ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
504 })
505 },
506 prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
507 next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
508 html: function(html){
509 return html === undefined ?
510 (this.length > 0 ? this[0].innerHTML : null) :
511 this.each(function(idx){
512 var originHtml = this.innerHTML
513 $(this).empty().append( funcArg(this, html, idx, originHtml) )
514 })
515 },
516 text: function(text){
517 return text === undefined ?
518 (this.length > 0 ? this[0].textContent : null) :
519 this.each(function(){ this.textContent = text })
520 },
521 attr: function(name, value){
522 var result
523 return (typeof name == 'string' && value === undefined) ?
524 (this.length == 0 || this[0].nodeType !== 1 ? undefined :
525 (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
526 (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
527 ) :
528 this.each(function(idx){
529 if (this.nodeType !== 1) return
530 if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
531 else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
532 })
533 },
534 removeAttr: function(name){
535 return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) })
536 },
537 prop: function(name, value){
538 return (value === undefined) ?
539 (this[0] && this[0][name]) :
540 this.each(function(idx){
541 this[name] = funcArg(this, value, idx, this[name])
542 })
543 },
544 data: function(name, value){
545 var data = this.attr('data-' + dasherize(name), value)
546 return data !== null ? deserializeValue(data) : undefined
547 },
548 val: function(value){
549 return (value === undefined) ?
550 (this[0] && (this[0].multiple ?
551 $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') :
552 this[0].value)
553 ) :
554 this.each(function(idx){
555 this.value = funcArg(this, value, idx, this.value)
556 })
557 },
558 offset: function(coordinates){
559 if (coordinates) return this.each(function(index){
560 var $this = $(this),
561 coords = funcArg(this, coordinates, index, $this.offset()),
562 parentOffset = $this.offsetParent().offset(),
563 props = {
564 top: coords.top - parentOffset.top,
565 left: coords.left - parentOffset.left
566 }
567
568 if ($this.css('position') == 'static') props['position'] = 'relative'
569 $this.css(props)
570 })
571 if (this.length==0) return null
572 var obj = this[0].getBoundingClientRect()
573 return {
574 left: obj.left + window.pageXOffset,
575 top: obj.top + window.pageYOffset,
576 width: obj.width,
577 height: obj.height
578 }
579 },
580 css: function(property, value){
581 if (arguments.length < 2 && typeof property == 'string')
582 return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property))
583
584 var css = ''
585 for (key in property)
586 if (!property[key] && property[key] !== 0)
587 this.each(function(){ this.style.removeProperty(dasherize(key)) })
588 else
589 css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
590
591 if (typeof property == 'string')
592 if (!value && value !== 0)
593 this.each(function(){ this.style.removeProperty(dasherize(property)) })
594 else
595 css = dasherize(property) + ":" + maybeAddPx(property, value)
596
597 return this.each(function(){ this.style.cssText += ';' + css })
598 },
599 index: function(element){
600 return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
601 },
602 hasClass: function(name){
603 return emptyArray.some.call(this, function(el){
604 return this.test(className(el))
605 }, classRE(name))
606 },
607 addClass: function(name){
608 return this.each(function(idx){
609 classList = []
610 var cls = className(this), newName = funcArg(this, name, idx, cls)
611 newName.split(/\s+/g).forEach(function(klass){
612 if (!$(this).hasClass(klass)) classList.push(klass)
613 }, this)
614 classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
615 })
616 },
617 removeClass: function(name){
618 return this.each(function(idx){
619 if (name === undefined) return className(this, '')
620 classList = className(this)
621 funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
622 classList = classList.replace(classRE(klass), " ")
623 })
624 className(this, classList.trim())
625 })
626 },
627 toggleClass: function(name, when){
628 return this.each(function(idx){
629 var newName = funcArg(this, name, idx, className(this))
630 ;(when === undefined ? !$(this).hasClass(newName) : when) ?
631 $(this).addClass(newName) : $(this).removeClass(newName)
632 })
633 },
634 scrollTop: function(){
635 if (!this.length) return
636 return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY
637 },
638 position: function() {
639 if (!this.length) return
640
641 var elem = this[0],
642 // Get *real* offsetParent
643 offsetParent = this.offsetParent(),
644 // Get correct offsets
645 offset = this.offset(),
646 parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
647
648 // Subtract element margins
649 // note: when an element has margin: auto the offsetLeft and marginLeft
650 // are the same in Safari causing offset.left to incorrectly be 0
651 offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
652 offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
653
654 // Add offsetParent borders
655 parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
656 parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
657
658 // Subtract the two offsets
659 return {
660 top: offset.top - parentOffset.top,
661 left: offset.left - parentOffset.left
662 }
663 },
664 offsetParent: function() {
665 return this.map(function(){
666 var parent = this.offsetParent || document.body
667 while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
668 parent = parent.offsetParent
669 return parent
670 })
671 }
672 }
673
674 // for now
675 $.fn.detach = $.fn.remove
676
677 // Generate the `width` and `height` functions
678 ;['width', 'height'].forEach(function(dimension){
679 $.fn[dimension] = function(value){
680 var offset, Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() })
681 if (value === undefined) return this[0] == window ? window['inner' + Dimension] :
682 this[0] == document ? document.documentElement['offset' + Dimension] :
683 (offset = this.offset()) && offset[dimension]
684 else return this.each(function(idx){
685 var el = $(this)
686 el.css(dimension, funcArg(this, value, idx, el[dimension]()))
687 })
688 }
689 })
690
691 function traverseNode(node, fun) {
692 fun(node)
693 for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
694 }
695
696 // Generate the `after`, `prepend`, `before`, `append`,
697 // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
698 adjacencyOperators.forEach(function(operator, operatorIndex) {
699 var inside = operatorIndex % 2 //=> prepend, append
700
701 $.fn[operator] = function(){
702 // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
703 var nodes = $.map(arguments, function(n){ return isObject(n) ? n : zepto.fragment(n) }),
704 parent, copyByClone = this.length > 1
705 if (nodes.length < 1) return this
706
707 return this.each(function(_, target){
708 parent = inside ? target : target.parentNode
709
710 // convert all methods to a "before" operation
711 target = operatorIndex == 0 ? target.nextSibling :
712 operatorIndex == 1 ? target.firstChild :
713 operatorIndex == 2 ? target :
714 null
715
716 nodes.forEach(function(node){
717 if (copyByClone) node = node.cloneNode(true)
718 else if (!parent) return $(node).remove()
719
720 traverseNode(parent.insertBefore(node, target), function(el){
721 if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
722 (!el.type || el.type === 'text/javascript') && !el.src)
723 window['eval'].call(window, el.innerHTML)
724 })
725 })
726 })
727 }
728
729 // after => insertAfter
730 // prepend => prependTo
731 // before => insertBefore
732 // append => appendTo
733 $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
734 $(html)[operator](this)
735 return this
736 }
737 })
738
739 zepto.Z.prototype = $.fn
740
741 // Export internal API functions in the `$.zepto` namespace
742 zepto.uniq = uniq
743 zepto.deserializeValue = deserializeValue
744 $.zepto = zepto
745
746 return $
747})()
748
749// If `$` is not yet defined, point it to `Zepto`
750window.Zepto = Zepto
751'$' in window || (window.$ = Zepto)
diff --git a/frontend/gamma/js/main_iPhone.js b/frontend/gamma/js/main.mobile.js
index 0644f68..60a32fa 100644
--- a/frontend/gamma/js/main_iPhone.js
+++ b/frontend/gamma/js/main.mobile.js
@@ -47,11 +47,18 @@ Clipperz.PM.RunTime = {};
47 47
48 48
49function run() { 49function run() {
50 MochiKit.DOM.removeElement('javaScriptAlert');
51 Clipperz.PM.Strings.Languages.initSetup(); 50 Clipperz.PM.Strings.Languages.initSetup();
52 51
53 Clipperz.PM.RunTime.mainController = new Clipperz.PM.UI.iPhone.Controllers.MainController(); 52 Clipperz.PM.RunTime.mainController = new Clipperz.PM.UI.Mobile.Controllers.MainController();
54 Clipperz.PM.RunTime.mainController.run(false); 53 Clipperz.PM.RunTime.mainController.run();
55} 54}
56 55
56// if (navigator.standalone == false) {
57 // window.localStorage.setItem('PIN', '1234');
58 // alert("Saved PIN");
59// } else {
60 // alert (window.localStorage.getItem('PIN'));
61// }
62
63
57MochiKit.DOM.addLoadEvent(run); 64MochiKit.DOM.addLoadEvent(run);