summaryrefslogtreecommitdiff
path: root/frontend/gamma/js
Side-by-side diff
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
@@ -56,48 +56,53 @@ MochiKit.Base.update(Clipperz.Base, {
)
);
},
//-------------------------------------------------------------------------
'isUrl': function (aValue) {
return (MochiKit.Base.urlRegExp.test(aValue));
},
'isEmail': function (aValue) {
return (MochiKit.Base.emailRegExp.test(aValue));
},
//-------------------------------------------------------------------------
'caseInsensitiveCompare': function (a, b) {
return MochiKit.Base.compare(a.toLowerCase(), b.toLowerCase());
},
'reverseComparator': function (aComparator) {
return MochiKit.Base.compose(function(aResult) { return -aResult; }, aComparator);
},
+ 'caseInsensitiveKeyComparator': function (aKey) {
+ return function (a, b) {
+ return MochiKit.Base.compare(a[aKey].toLowerCase(), b[aKey].toLowerCase());
+ }
+ },
//-------------------------------------------------------------------------
/*
'dependsOn': function(module, deps) {
if (!(module in Clipperz)) {
MochiKit[module] = {};
}
if (typeof(dojo) != 'undefined') {
dojo.provide('Clipperz.' + module);
}
for (var i = 0; i < deps.length; i++) {
if (typeof(dojo) != 'undefined') {
dojo.require('Clipperz.' + deps[i]);
}
if (typeof(JSAN) != 'undefined') {
JSAN.use('Clipperz.' + deps[i], []);
}
if (!(deps[i] in Clipperz)) {
throw 'Clipperz.' + module + ' depends on Clipperz.' + deps[i] + '!'
}
}
},
*/
//-------------------------------------------------------------------------
diff --git a/frontend/gamma/js/Clipperz/PM/Connection.js b/frontend/gamma/js/Clipperz/PM/Connection.js
index b4e8aaa..a05a310 100644
--- a/frontend/gamma/js/Clipperz/PM/Connection.js
+++ b/frontend/gamma/js/Clipperz/PM/Connection.js
@@ -20,48 +20,49 @@ refer to http://www.clipperz.com.
* You should have received a copy of the GNU Affero General Public
License along with Clipperz Community Edition. If not, see
<http://www.gnu.org/licenses/>.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
//-----------------------------------------------------------------------------
//
// Abstract C O N N E C T I O N class
//
//-----------------------------------------------------------------------------
Clipperz.PM.Connection = function (args) {
args = args || {};
this._proxy = args.proxy || Clipperz.PM.Proxy.defaultProxy;
this._getCredentialsFunction = args.getCredentialsFunction;
this._clipperz_pm_crypto_version = null;
this._connectionId = null;
this._sharedSecret = null;
+ this._serverLockValue = null;
return this;
}
Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, {
'toString': function() {
return "Connection [" + this.version() + "]";
},
//=========================================================================
'version': function() {
throw Clipperz.Base.exception.AbstractMethod;
},
'clipperz_pm_crypto_version': function() {
if (this._clipperz_pm_crypto_version == null) {
var connectionVersions;
var versions;
var version;
var i, c;
version = null;
@@ -125,48 +126,58 @@ MochiKit.Logging.logError("### Connection.defaultErrorHandler: " + anErrorString
'serverSideUserCredentials': function() {
throw Clipperz.Base.exception.AbstractMethod;
},
//=========================================================================
'sharedSecret': function () {
return this._sharedSecret;
},
'setSharedSecret': function (aValue) {
this._sharedSecret = aValue;
},
//-------------------------------------------------------------------------
'connectionId': function() {
return this._connectionId;
},
'setConnectionId': function(aValue) {
this._connectionId = aValue;
},
+ //-------------------------------------------------------------------------
+
+ 'serverLockValue': function () {
+ return this._serverLockValue;
+ },
+
+ 'setServerLockValue': function (aValue) {
+ this._serverLockValue = aValue;
+ },
+
//=========================================================================
/*
// TODO: ?????
'oneTimePassword': function() {
return this._oneTimePassword;
},
'setOneTimePassword': function(aValue) {
this._oneTimePassword = aValue;
},
*/
//=========================================================================
'reset': function() {
this.setSharedSecret(null);
this.setConnectionId(null);
},
//=========================================================================
__syntaxFix__: "syntax fix"
}
);
@@ -299,55 +310,54 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
args = {
'message': 'oneTimePassword',
'version': Clipperz.PM.Connection.communicationProtocol.currentVersion,
'parameters': {
'oneTimePasswordKey': Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(someParameters['username'], normalizedOTP),
'oneTimePasswordKeyChecksum': Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(someParameters['username'], normalizedOTP)
}
}
return Clipperz.Async.callbacks("Connction.redeemOTP", [
MochiKit.Base.method(this.proxy(), 'handshake', args),
function(aResult) {
return Clipperz.PM.Crypto.deferredDecrypt({
value: aResult['data'],
key: normalizedOTP,
version:aResult['version']
});
},
function(aResult) {
return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString();
}
], {trace:false})
},
- 'login': function(/*anUsername, aPassphrase*/) {
+ 'login': function(isReconnecting) {
var deferredResult;
var cryptoVersion;
var srpConnection;
cryptoVersion = this.clipperz_pm_crypto_version();
-
deferredResult = new Clipperz.Async.Deferred("Connection.login", {trace:false});
deferredResult.addCallback(this.getCredentialsFunction());
deferredResult.addMethod(this, 'normalizedCredentials');
// deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_sendingCredentials');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
deferredResult.addCallback(MochiKit.Base.bind(function(someCredentials) {
srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() });
}, this));
deferredResult.addCallback(function() {
var result;
result = {
message: 'connect',
version: cryptoVersion,
parameters: {
C: srpConnection.C(),
A: srpConnection.A().asString(16)
// reconnecting: this.connectionId()
}
};
// TODO: ?????
// if (isReconnecting == true) {
// args.parameters['reconnecting'] = aConnection.connectionId();
@@ -378,100 +388,113 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
};
return result;
});
deferredResult.addMethod(this.proxy(), 'handshake');
deferredResult.addCallback(function(someParameters) {
var result;
if (someParameters['M2'] == srpConnection.M2()) {
result = MochiKit.Async.succeed(someParameters);
} else {
result = MochiKit.Async.fail(Clipperz.PM.Connection.exception.WrongChecksum);
}
return result;
});
deferredResult.addCallback(MochiKit.Base.bind(function(someParameters) {
this.setConnectionId(someParameters['connectionId']);
this.setSharedSecret(srpConnection.K());
// TODO: ?????
// if (this.oneTimePassword() != null) {
/// ?? result = this.user().oneTimePasswordManager().archiveOneTimePassword(this.oneTimePassword()));
// }
+
+ if ((isReconnecting == true) && (this.serverLockValue() != someParameters['lock'])) {
+ throw Clipperz.PM.Connection.exception.StaleData;
+ } else {
+ this.setServerLockValue(someParameters['lock']);
+ }
+
return someParameters;
}, this));
// deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_loggedIn');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
deferredResult.addCallback(MochiKit.Async.succeed, {result:"done"});
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'logout': function() {
return Clipperz.Async.callbacks("Connection.logout", [
MochiKit.Base.method(this, 'setSharedSecret'),
MochiKit.Base.method(this.proxy(), 'logout', {})
], {trace:false});
},
//=========================================================================
'ping': function () {
// TODO: ping the server in order to have a valid session
},
//=========================================================================
'message': function(aMessageName, someParameters) {
var args;
+ var parameters;
+
+ parameters = someParameters || {};
+ if (typeof(parameters['user']) != 'undefined') {
+ parameters['user']['lock'] = this.serverLockValue();
+ }
//console.log(">>> Connection.message", aMessageName, someParameters);
args = {
message: aMessageName,
srpSharedSecret: this.sharedSecret(),
- parameters: (someParameters || {})
+// parameters: (someParameters || {})
+ parameters: parameters
}
return this.sendMessage(args);
},
//-------------------------------------------------------------------------
'sendMessage': function(someArguments) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false});
deferredResult.addMethod(this.proxy(), 'message', someArguments);
deferredResult.addCallback(MochiKit.Base.bind(function(res) {
if (typeof(res['lock']) != 'undefined') {
-// TODO: ?????
-// ?? this.user().setLock(res['lock']);
+ this.setServerLockValue(res['lock']);
}
return res;
}, this));
deferredResult.addErrback(MochiKit.Base.method(this, 'messageExceptionHandler'), someArguments);
deferredResult.callback();
return deferredResult
},
//-------------------------------------------------------------------------
'messageExceptionHandler': function(anOriginalMessageArguments, anError) {
var result;
console.log(">>> Connection.messageExceptionHandler", anError, anError.message);
if (anError instanceof MochiKit.Async.CancelledError) {
result = anError;
} else {
if ((anError.message == 'Trying to communicate without an active connection') ||
(anError.message == 'No tollManager available for current session')
) {
result = this.reestablishConnection(anOriginalMessageArguments);
} else if (anError.message == 'Session with stale data') {
@@ -566,48 +589,49 @@ Clipperz.PM.Connection.SRP['1.1'].prototype = MochiKit.Base.update(new Clipperz.
'normalizedCredentials': function(someValues) {
var result;
result = {}
result['username'] = this.hash()(new Clipperz.ByteArray(someValues['username'] + someValues['password'])).toHexString().substring(2);
result['password'] = this.hash()(new Clipperz.ByteArray(someValues['password'] + someValues['username'])).toHexString().substring(2);
return result;
},
//-----------------------------------------------------------------------------
'hash': function() {
return Clipperz.PM.Crypto.encryptingFunctions.versions['0.2'].hash;
},
//-----------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
Clipperz.PM.Connection.exception = {
WrongChecksum: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue"),
+ StaleData: new MochiKit.Base.NamedError("Stale data"),
UnexpectedRequest: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.UnexpectedRequest")
};
Clipperz.PM.Connection.communicationProtocol = {
'currentVersion': '0.2',
'versions': {
'0.1': Clipperz.PM.Connection.SRP['1.0'], //Clipperz.Crypto.SRP.versions['1.0'].Connection,
'0.2': Clipperz.PM.Connection.SRP['1.1'] //Clipperz.Crypto.SRP.versions['1.1'].Connection
},
'fallbackVersions': {
// 'current': '0.1',
'0.2': '0.1',
'0.1': null
}
};
MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, {
'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
});
MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.fallbackVersions, {
'current': Clipperz.PM.Connection.communicationProtocol.fallbackVersions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
});
diff --git a/frontend/gamma/js/Clipperz/PM/DataModel/User.js b/frontend/gamma/js/Clipperz/PM/DataModel/User.js
index 72d4006..646ce21 100644
--- a/frontend/gamma/js/Clipperz/PM/DataModel/User.js
+++ b/frontend/gamma/js/Clipperz/PM/DataModel/User.js
@@ -23,106 +23,106 @@ refer to http://www.clipperz.com.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
//#############################################################################
Clipperz.PM.DataModel.User = function (args) {
args = args || {};
Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments);
this._username = args.username || null;
this._getPassphraseFunction = args.getPassphraseFunction || null;
this._data = null;
this._connection = null;
this._connectionVersion = 'current';
this._serverData = null;
- this._serverLockValue = null;
+// this._serverLockValue = null;
this._transientState = null;
this._deferredLocks = {
'passphrase': new MochiKit.Async.DeferredLock(),
'serverData': new MochiKit.Async.DeferredLock(),
// 'recordsIndex': new MochiKit.Async.DeferredLock(),
// 'directLoginsIndex': new MochiKit.Async.DeferredLock()
// 'preferences': new MochiKit.Async.DeferredLock()
// 'oneTimePasswords': new MochiKit.Async.DeferredLock()
'__syntaxFix__': 'syntax fix'
};
return this;
}
Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'toString': function () {
return "Clipperz.PM.DataModel.User - " + this.username();
},
//-------------------------------------------------------------------------
'username': function () {
return this._username;
},
'setUsername': function (aValue) {
this._username = aValue;
},
//-------------------------------------------------------------------------
'displayName': function() {
return "" + this.username() + "";
},
//-------------------------------------------------------------------------
'data': function () {
if (this._data == null) {
this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/);
};
return this._data;
},
//-------------------------------------------------------------------------
-
+/*
'serverLockValue': function () {
return this._serverLockValue;
},
'setServerLockValue': function (aValue) {
this._serverLockValue = aValue;
},
-
+*/
//-------------------------------------------------------------------------
'transientState': function () {
if (this._transientState == null) {
this._transientState = {}
}
return this._transientState;
},
'resetTransientState': function (isCommitting) {
this._transientState = null;
},
//-------------------------------------------------------------------------
'deferredLockForSection': function(aSectionName) {
return this._deferredLocks[aSectionName];
},
//-------------------------------------------------------------------------
'getPassphrase': function() {
var deferredResult;
@@ -199,76 +199,76 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
}),
'oneTimePasswords': new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
'name': 'preferences',
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
})
}
};
// this._serverLockValue = Clipperz.PM.Crypto.randomKey();
},
//.........................................................................
'registerAsNewAccount': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false});
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
deferredResult.addMethod(this, 'initialSetupWithNoData')
deferredResult.addMethod(this, 'getPassphrase');
deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
deferredResult.addMethod(this.connection(), 'register');
- deferredResult.addCallback(MochiKit.Base.itemgetter('lock'));
- deferredResult.addMethod(this, 'setServerLockValue');
+// deferredResult.addCallback(MochiKit.Base.itemgetter('lock'));
+// deferredResult.addMethod(this, 'setServerLockValue');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered');
// deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure'));
deferredResult.callback();
return deferredResult;
},
//-------------------------------------------------------------------------
'login': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false});
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
deferredResult.addMethod(this, 'getPassphrase');
deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue);
deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [
MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}),
MochiKit.Base.method(this, 'getCredentials'),
MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'),
MochiKit.Base.method(this.data(), 'setValue', 'passphrase')
], []));
deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase'));
- deferredResult.addMethod(this.connection(), 'login');
+ deferredResult.addMethod(this.connection(), 'login', false);
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn');
deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
deferredResult.callback();
return deferredResult;
},
//.........................................................................
'handleConnectionFallback': function(aValue) {
var result;
if (aValue instanceof MochiKit.Async.CancelledError) {
result = aValue;
} else {
this.setConnectionVersion(Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()]);
if (this.connectionVersion() != null) {
result = new Clipperz.Async.Deferred("User.handleConnectionFallback - retry");
result.addMethod(this, 'login');
result.callback();
} else {
@@ -308,49 +308,49 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
if (anHeader.charAt(0) == '{') {
var headerData;
headerData = Clipperz.Base.evalJSON(anHeader);
result = headerData['version'];
} else {
result = 'LEGACY';
}
return result;
},
//-------------------------------------------------------------------------
'unpackServerData': function (someServerData) {
var unpackedData;
var headerVersion;
var recordsIndex;
var preferences;
var oneTimePasswords;
//console.log(">>> ***************** user.unpackServerData", someServerData);
- this.setServerLockValue(someServerData['lock']);
+// this.setServerLockValue(someServerData['lock']);
headerVersion = this.headerFormatVersion(someServerData['header']);
switch (headerVersion) {
case 'LEGACY':
var legacyHeader;
legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
'remoteData': {
'data': someServerData['header'],
'version': someServerData['version'],
'recordsStats': someServerData['recordsStats']
},
// 'encryptedDataKeypath': 'data',
// 'encryptedVersionKeypath': 'version',
'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
});
recordsIndex = legacyHeader;
preferences = legacyHeader;
oneTimePasswords = legacyHeader;
break;
case '0.1':
@@ -641,50 +641,50 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
deferredResult.collectResults({
'header': [
MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasPendingChanges'),
MochiKit.Base.values
],
'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasPendingChanges')
});
deferredResult.addCallback(Clipperz.Async.or);
deferredResult.callback();
// recordsIndex = legacyHeader;
// preferences = legacyHeader;
// oneTimePasswords = legacyHeader;
return deferredResult;
},
//=========================================================================
'commitTransientState': function () {
return Clipperz.Async.callbacks("User.commitTransientState", [
MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'),
MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'),
MochiKit.Base.method(this, 'transientState'),
- MochiKit.Base.itemgetter('lock'),
- MochiKit.Base.method(this, 'setServerLockValue'),
+// MochiKit.Base.itemgetter('lock'),
+// MochiKit.Base.method(this, 'setServerLockValue'),
MochiKit.Base.method(this, 'resetTransientState', true)
], {trace:false});
},
//-------------------------------------------------------------------------
'revertChanges': function () {
return Clipperz.Async.callbacks("User.revertChanges", [
MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'),
MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'),
MochiKit.Base.method(this, 'resetTransientState', false)
], {trace:false});
},
//=========================================================================
'deleteAllCleanTextData': function () {
return Clipperz.Async.callbacks("User.deleteAllCleanTextData", [
MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'deleteAllCleanTextData'),
MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'deleteAllCleanTextData'),
MochiKit.Base.method(this.data(), 'removeAllData'),
MochiKit.Base.method(this, 'resetTransientState', false)
], {trace:false});
@@ -719,49 +719,49 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'prepareRemoteDataWithKey': function (aKey /*, aCurrentKey*/) {
var deferredResult;
var result;
result = {};
deferredResult = new Clipperz.Async.Deferred("User.prepareRemoteDataWithKey", {trace:false});
deferredResult.addMethod(this, 'invokeMethodNamedOnHeader', 'prepareRemoteDataWithKey', aKey /*, aCurrentKey*/);
deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) {
var header;
header = {};
header['records'] = someHeaderPackedData['recordIndex']['records'];
header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
header['preferences'] = {'data': someHeaderPackedData['preferences']['data']}; // this._serverData['header']['preferences']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['preferences']; // ???????????
header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']}; // this._serverData['header']['oneTimePasswords']; // Clipperz.Base.evalJSON(this._serverData['header']['data'])['oneTimePasswords']; // ???????????
header['version'] = '0.1';
aResult['header'] = Clipperz.Base.serializeJSON(header);
aResult['statistics'] = this._serverData['statistics']; // "someHeaderPackedData['statistics']['data']";
return aResult;
}, this), result);
deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
- deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue());
+// deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue());
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'saveChanges': function () {
var deferredResult;
var messageParameters;
messageParameters = {};
deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false});
deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex');
deferredResult.addCallback(MochiKit.Base.methodcaller('prepareRemoteDataForChangedRecords'));
deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'records');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
deferredResult.addMethod(this, 'getPassphrase');
deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'user');
deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
diff --git a/frontend/gamma/js/Clipperz/PM/PIN.js b/frontend/gamma/js/Clipperz/PM/PIN.js
new file mode 100644
index 0000000..bc932b2
--- a/dev/null
+++ b/frontend/gamma/js/Clipperz/PM/PIN.js
@@ -0,0 +1,134 @@
+/*
+
+Copyright 2008-2011 Clipperz Srl
+
+This file is part of Clipperz Community Edition.
+Clipperz Community Edition is an online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz Community Edition is free software: you can redistribute
+ it and/or modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation, either version
+ 3 of the License, or (at your option) any later version.
+
+* Clipperz Community Edition is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz Community Edition. If not, see
+ <http://www.gnu.org/licenses/>.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.PIN) == 'undefined') { Clipperz.PM.PIN = {}; }
+
+MochiKit.Base.update(Clipperz.PM.PIN, {
+
+ //-------------------------------------------------------------------------
+
+ '__repr__': function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return this.__repr__();
+ },
+
+ 'CREDENTIALS': 'CLIPPERZ.CREDENTIALS',
+ 'FAILURE_COUNT': 'CLIPPERZ.FAILED_LOGIN_COUNT',
+ 'ALLOWED_RETRY': 3,
+
+ //-------------------------------------------------------------------------
+
+ 'isSet': function () {
+ return (this.storedCredentials() != null);
+ },
+
+ 'storedCredentials': function () {
+ return localStorage[this.CREDENTIALS];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordFailedAttempt': function () {
+ var failureCount;
+ var result;
+
+ failureCount = localStorage[this.FAILURE_COUNT];
+
+ if (failureCount == null) {
+ failureCount = 0
+ }
+
+ failureCount ++;
+
+ if (failureCount < this.ALLOWED_RETRY) {
+ localStorage[this.FAILURE_COUNT] = failureCount;
+ result = failureCount;
+ } else {
+ this.removeLocalCredentials();
+ result = -1;
+ }
+
+ return result;
+ },
+
+ 'resetFailedAttemptCount': function () {
+ localStorage.removeItem(this.FAILURE_COUNT);
+ },
+
+ 'failureCount': function () {
+ return localStorage[this.FAILURE_COUNT];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deriveKeyFromPin': function (aPIN) {
+ return Clipperz.Crypto.SHA.sha256(new Clipperz.ByteArray(aPIN));
+ },
+
+ 'credentialsWithPIN': function (aPIN) {
+ var byteArrayValue;
+ var decryptedValue;
+ var result;
+
+ byteArrayValue = (new Clipperz.ByteArray()).appendBase64String(localStorage[this.CREDENTIALS]);
+ decryptedValue = Clipperz.Crypto.AES.decrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).asString();
+ try {
+ result = Clipperz.Base.evalJSON(decryptedValue);
+ } catch (error) {
+ result = {'username':'fakeusername', 'passphrase':'fakepassphrase'};
+ }
+
+ return result;
+ },
+
+ 'setCredentialsWithPIN': function (aPIN, someCredentials) {
+ var encodedValue;
+ var byteArrayValue;
+ var encryptedValue;
+
+ encodedValue = Clipperz.Base.serializeJSON(someCredentials);
+ byteArrayValue = new Clipperz.ByteArray(encodedValue);
+ encryptedValue = Clipperz.Crypto.AES.encrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).toBase64String();
+
+ localStorage[this.CREDENTIALS] = encryptedValue;
+ },
+
+ 'removeLocalCredentials': function () {
+ localStorage.removeItem(this.CREDENTIALS);
+ localStorage.removeItem(this.FAILURE_COUNT);
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js
new file mode 100644
index 0000000..803c590
--- a/dev/null
+++ b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.OfflineCache.js
@@ -0,0 +1,65 @@
+/*
+
+Copyright 2008-2011 Clipperz Srl
+
+This file is part of Clipperz Community Edition.
+Clipperz Community Edition is an online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz Community Edition is free software: you can redistribute
+ it and/or modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation, either version
+ 3 of the License, or (at your option) any later version.
+
+* Clipperz Community Edition is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz Community Edition. If not, see
+ <http://www.gnu.org/licenses/>.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Proxy.OfflineCache = function(args) {
+ args = args || {};
+
+ Clipperz.PM.Proxy.Offline.superclass.constructor.call(this, args);
+
+// this._dataStore = args.dataStore || new Clipperz.PM.Proxy.Offline.DataStore(args);
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.OfflineCache, Clipperz.PM.Proxy, {
+
+ 'toString': function () {
+ return "Clipperz.PM.Proxy.OfflineCache";
+ },
+
+ //-------------------------------------------------------------------------
+
+// 'dataStore': function () {
+// return this._dataStore;
+// },
+
+ //-------------------------------------------------------------------------
+
+ 'sendMessage': function(aFunctionName, someParameters) {
+ throw Clipperz.Base.exception.MethodNotImplementedYet;
+// return this.dataStore().processMessage(aFunctionName, someParameters);
+ },
+
+ //-------------------------------------------------------------------------
+
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js
index 2a03fdf..b9d7adf 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/BaseComponent.js
@@ -385,48 +385,52 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.BaseComponent, /*Ext.Compo
},
//-------------------------------------------------------------------------
'getId': function(aValue) {
var result;
if (typeof(aValue) != 'undefined') {
result = this._ids[aValue];
if (typeof(result) == 'undefined') {
_Clipperz_PM_Components_base_id_ ++;
result = "Clipperz_PM_Components_" + aValue + "_" + _Clipperz_PM_Components_base_id_;
this._ids[aValue] = result;
}
} else {
// result = Clipperz.PM.UI.Common.Components.BaseComponent.superclass.getId.call(this);
throw "call to BaseComponent.getId with an undefined value";
}
return result;
},
+ 'getAnchor': function (aValue) {
+ return '#' + this.getId(aValue);
+ },
+
//-------------------------------------------------------------------------
'getElement': function(aValue) {
return Clipperz.DOM.get(this.getId(aValue));
},
//-------------------------------------------------------------------------
'hideElement': function(anElementName) {
MochiKit.Style.hideElement(this.getElement(anElementName));
},
'showElement': function(anElementName) {
MochiKit.Style.showElement(this.getElement(anElementName));
},
//-------------------------------------------------------------------------
'activate': function () {
this._isActive = true;
},
'deactivate': function () {
this._isActive = false;
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js
index 716d851..1010c9d 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/Button.js
@@ -41,65 +41,73 @@ Clipperz.PM.UI.Common.Components.Button = function(args) {
//=============================================================================
Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.Button, Clipperz.PM.UI.Common.Components.BaseComponent, {
//-------------------------------------------------------------------------
'toString': function () {
return "Clipperz.PM.UI.Common.Components.Button component";
},
//-------------------------------------------------------------------------
'text': function () {
return this._text;
},
'isDefault': function () {
return this._isDefault;
},
//-------------------------------------------------------------------------
'renderSelf': function () {
- this.append(this.element(), {tag:'div', id:this.getId('wrapper'), cls:'button_wrapper', children:[
+/*
+ this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button_wrapper', children:[
{tag:'div', id:this.getId('bodyWrapper'), cls:'button_bodyWrapper', children:[
{tag:'div', id:this.getId('body'), cls:'button_body', children:[
{tag:'span', html:this.text()}
]},
{tag:'div', id:this.getId('footer'), cls:'button_footer'}
]}
]});
+*/
+/*
+ this.append(this.element(), {tag:'div', id:this.getId('button'), cls:'button', children:[
+ {tag:'span', html:this.text()}
+ ]});
+*/
+ this.append(this.element(), {tag:'a', id:this.getId('button'), cls:'button', html:this.text()});
if (this.isDefault()) {
- MochiKit.DOM.addElementClass(this.getId('wrapper'), 'default');
+ MochiKit.DOM.addElementClass(this.getId('button'), 'default');
}
- MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseenter', this, 'handleOnMouseEnter');
- MochiKit.Signal.connect(this.getId('wrapper'), 'onmouseleave', this, 'handleOnMouseLeave');
- MochiKit.Signal.connect(this.getId('wrapper'), 'onmousedown', this, 'handleOnMouseDown');
- MochiKit.Signal.connect(this.getId('wrapper'), 'onclick', this, 'handleOnClick');
+// MochiKit.Signal.connect(this.getId('button'), 'onmouseenter', this, 'handleOnMouseEnter');
+// MochiKit.Signal.connect(this.getId('button'), 'onmouseleave', this, 'handleOnMouseLeave');
+// MochiKit.Signal.connect(this.getId('button'), 'onmousedown', this, 'handleOnMouseDown');
+ MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'handleOnClick');
},
//-------------------------------------------------------------------------
-
+/*
'handleOnMouseEnter': function (anEvent) {
- MochiKit.DOM.addElementClass(this.getId('wrapper'), 'hover');
+ MochiKit.DOM.addElementClass(this.getId('button'), 'hover');
},
'handleOnMouseLeave': function (anEvent) {
- MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'hover');
- MochiKit.DOM.removeElementClass(this.getId('wrapper'), 'clicked');
+ MochiKit.DOM.removeElementClass(this.getId('button'), 'hover');
+ MochiKit.DOM.removeElementClass(this.getId('button'), 'clicked');
},
'handleOnMouseDown': function (anEvent) {
- MochiKit.DOM.addElementClass(this.getId('wrapper'), 'clicked');
+ MochiKit.DOM.addElementClass(this.getId('button'), 'clicked');
},
-
+*/
'handleOnClick': function (anEvent) {
MochiKit.Signal.signal(this, 'onclick', anEvent);
},
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js
index 1992154..1d816a9 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Common/Components/SimpleMessagePanel.js
@@ -77,93 +77,111 @@ Clipperz.Base.extend(Clipperz.PM.UI.Common.Components.SimpleMessagePanel, Clippe
}
},
//-------------------------------------------------------------------------
'text': function () {
return this._text;
},
'setText': function (aValue) {
this._text = aValue;
if (this.getElement('text') != null) {
this.getElement('text').innerHTML = aValue;
}
},
//-------------------------------------------------------------------------
'type': function () {
return this._type;
},
'setType': function (aValue) {
- if (this.getElement('icon') != null) {
- MochiKit.DOM.removeElementClass(this.getId('icon'), this._type);
- MochiKit.DOM.addElementClass(this.getId('icon'), aValue);
- }
+// if (this.getElement('icon') != null) {
+// MochiKit.DOM.removeElementClass(this.getId('icon'), this._type);
+// MochiKit.DOM.addElementClass(this.getId('icon'), aValue);
+// }
this._type = aValue;
},
+ 'icon': function () {
+ var type = this.type();
+ var result;
+
+ if (type == 'ALERT') {
+ result = '!';
+ } else if (type == 'INFO') {
+ result = 'i';
+ } else if (type == 'ERROR') {
+ result = '!';
+ }
+
+ return result;
+ },
+
//-------------------------------------------------------------------------
'buttons': function () {
return this._buttons;
},
'setButtons': function (someValues) {
MochiKit.Iter.forEach(this.buttonComponents(), MochiKit.Base.methodcaller('clear'));
this._buttons = someValues;
if (this.getElement('buttonArea') != null) {
this.renderButtons();
}
},
//.........................................................................
'buttonComponents': function () {
return this._buttonComponents;
},
//-------------------------------------------------------------------------
'renderSelf': function() {
this.append(this.element(), {tag:'div', cls:'SimpleMessagePanel', id:this.getId('panel'), children: [
- {tag:'div', cls:'header', children:[]},
+// {tag:'div', cls:'header', children:[]},
{tag:'div', cls:'body', children:[
- {tag:'div', id:this.getId('icon'), cls:'img ' + this.type(), children:[{tag:'div'}]},
+// {tag:'div', id:this.getId('icon'), cls:'img ' + this.type(), children:[{tag:'div'}]},
+ {tag:'div', /*id:this.getId('icon'),*/ cls:'img ' + this.type(), children:[{tag:'canvas', id:this.getId('icon')}]},
{tag:'h3', id:this.getId('title'), html:this.title()},
{tag:'p', id:this.getId('text'), html:this.text()},
{tag:'div', id:this.getId('container')},
{tag:'div', id:this.getId('buttonArea'), cls:'buttonArea', children:[]}
- ]},
- {tag:'div', cls:'footer', children:[]}
+ ]}
+// {tag:'div', cls:'footer', children:[]}
]});
+ Clipperz.PM.UI.Canvas.marks[this.icon()](this.getElement('icon'), "#ffffff");
+
MochiKit.Signal.connect(this.getId('panel'), 'onkeydown', this, 'keyDownHandler');
this.renderButtons();
},
//-------------------------------------------------------------------------
'renderButtons': function () {
this.getElement('buttonArea').innerHTML = '';
MochiKit.Base.map(MochiKit.Base.bind(function (aButton) {
var buttonElement;
var buttonComponent;
// element = this.append(this.getElement('buttonArea'), {tag:'div', cls:'button' + (aButton['isDefault'] === true ? ' default' : ''), children:[
// {tag:'a', href:'#'/*, id:this.getId('buttonLink')*/, html:aButton['text']}
// ]});
buttonElement = this.append(this.getElement('buttonArea'), {tag:'div'});
buttonComponent = new Clipperz.PM.UI.Common.Components.Button({'element':buttonElement, 'text':aButton['text'], 'isDefault':aButton['isDefault']});
this.buttonComponents().push(buttonComponent);
MochiKit.Signal.connect(buttonComponent, 'onclick', MochiKit.Base.method(this, 'buttonEventHandler', aButton));
}, this), MochiKit.Iter.reversed(this.buttons()));
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js
new file mode 100644
index 0000000..32dfa63
--- a/dev/null
+++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardDetail.js
@@ -0,0 +1,299 @@
+/*
+
+Copyright 2008-2011 Clipperz Srl
+
+This file is part of Clipperz Community Edition.
+Clipperz Community Edition is an online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz Community Edition is free software: you can redistribute
+ it and/or modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation, either version
+ 3 of the License, or (at your option) any later version.
+
+* Clipperz Community Edition is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz Community Edition. If not, see
+ <http://www.gnu.org/licenses/>.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
+
+Clipperz.PM.UI.Mobile.Components.CardDetail = function(args) {
+ args = args || {};
+
+ Clipperz.PM.UI.Mobile.Components.CardDetail.superclass.constructor.apply(this, arguments);
+
+// this._cardReference = null;
+
+ return this;
+}
+
+//=============================================================================
+
+Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, {
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return "Clipperz.PM.UI.Mobile.Components.CardDetail component";
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'cardReference': function () {
+ return this._cardReference;
+ },
+
+ 'setCardReference': function (aValue) {
+ this._cardReference = aValue;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'renderSelf': function () {
+console.log("CardDetail.renderSelf");
+ this.append(this.element(), {tag:'div', cls:'cardDetail', children:[
+ {tag:'div', cls:'toolbar', children:[
+ {tag:'a', href:'#', cls:'back', html:"List"},
+ {tag:'h1', id:this.getId('cardTitle'), html:"…"}
+ ]},
+ {tag:'div', cls:'scroll', id:this.getId('cardDetails'), children:[
+ ]}
+ ]});
+ },
+/*
+ 'renderSelf': function() {
+ this.append(this.element(), [
+ {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[
+ {tag:'div', id:this.getId('progressBar')} //,
+ ]}
+ ]);
+
+ this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
+ MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0);
+ },
+*/
+
+ 'setTitle': function (aValue) {
+ this.getElement('cardTitle').innerHTML = aValue;
+ },
+
+ 'fieldListElement': function () {
+ var result;
+
+ result = this.getElement('fieldList');
+ if (result == null) {
+ result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('fieldList')});
+ }
+
+ return result;
+ },
+
+ 'renderFieldValues': function (someFieldValues) {
+ var fieldClass;
+
+ if ((someFieldValues['actionType'] != 'NONE') || (someFieldValues['label'] != '') && (someFieldValues['value'] != '')) {
+ if (someFieldValues['isHidden'] == true) {
+ fieldClass = 'password';
+ } else {
+ fieldClass = '';
+ }
+
+ this.append(this.fieldListElement(), {tag:'li', cls:'cardField', children:[
+ {tag:'a', href:'#', cls:fieldClass, html:someFieldValues['value'], children:[
+ {tag:'small', cls:'label', html:someFieldValues['label']}
+ ]}
+ ]})
+ }
+ },
+
+ 'addField': function (aField) {
+ var deferredResult;
+ var fieldValues;
+
+ fieldValues = {};
+ deferredResult = new Clipperz.Async.Deferred("CardDetail.addField", {trace:false});
+ deferredResult.addMethod(aField, 'label');
+ deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
+ deferredResult.addMethod(aField, 'value');
+ deferredResult.addCallback(function (aValue) { fieldValues['value'] = aValue; });
+ deferredResult.addMethod(aField, 'actionType');
+ deferredResult.addCallback(function (aValue) { fieldValues['actionType'] = aValue; });
+ deferredResult.addMethod(aField, 'isHidden');
+ deferredResult.addCallback(function (aValue) { fieldValues['isHidden'] = aValue; });
+ deferredResult.addMethod(this, 'renderFieldValues', fieldValues);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLoginElement': function () {
+ var result;
+
+ result = this.getElement('directLoginList');
+ if (result == null) {
+ this.append(this.getElement('cardDetails'), {tag:'h2', html:"Direct login"});
+ result = this.append(this.getElement('cardDetails'), {tag:'ul', cls:'rounded', id:this.getId('directLoginList')});
+ }
+
+ return result;
+ },
+
+ 'addDirectLogin': function (aDirectLogin) {
+ this.append(this.directLoginElement(), {tag:'li', cls:'directLogin forward', children:[
+ {tag:'a', href:'#', html:"direct login", children:[
+ {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:'http://www.clipperz.com/favicon.ico'}]}
+ ]}
+ ]})
+
+console.log("ADD DIRECT LOGIN", aDirectLogin);
+ },
+
+ //=========================================================================
+
+ 'showCard': function (aCard) {
+ var deferredResult;
+
+// this.render();
+
+console.log("CardDetail.showCard", aCard);
+ deferredResult = new Clipperz.Async.Deferred("CardDetail.showCard", {trace:false});
+ deferredResult.addMethod(aCard, 'label');
+ deferredResult.addMethod(this, 'setTitle');
+
+ deferredResult.addMethod(aCard, 'fields');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addField'));
+
+ deferredResult.addMethod(aCard, 'directLogins');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'addDirectLogin'));
+
+
+ deferredResult.callback();
+
+ return deferredResult;
+// return Clipperz.Async.callbacks("CardDialogController.updateComponentState", [
+// MochiKit.Base.method(this.record(), 'hasPendingChanges'),
+// MochiKit.Base.method(this.cardDialogComponent(), 'setShouldEnableSaving'),
+//
+// MochiKit.Base.method(this.record(), 'label'),
+// MochiKit.Base.method(this.cardDialogComponent(), 'setTitle'),
+// MochiKit.Base.method(this.record(), 'notes'),
+// MochiKit.Base.method(this.cardDialogComponent(), 'setNotes'),
+//
+// MochiKit.Base.method(this.record(), 'fields'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithField')),
+//
+// MochiKit.Base.method(this.record(), 'directLogins'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addCardDialogComponentWithDirectLogin')),
+//
+// MochiKit.Base.method(this.cardDialogComponent(), 'resetNewFieldInputs'),
+// MochiKit.Base.noop
+// ], {trace:false});
+
+ },
+
+ //=========================================================================
+
+ 'showCardDetails': function (someData) {
+ this.element().innerHTML = '';
+ this.append(this.element(), [
+ {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) {
+ return {tag:'div', cls:'row', children:[
+ {tag:'label', html:aFieldData['label']},
+// {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), html:aFieldData['value']}
+ {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), children:[
+ {tag:'div', children:[{tag:'p', html:aFieldData['value']}]}
+ ]}
+// {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), value:aFieldData['value'], disabled:true}
+
+ ]}
+ }, someData['fields'])}
+ ]);
+
+ MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) {
+ MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); })
+ }, this));
+
+ if (someData['directLogins'].length > 0) {
+ this.append(this.element(), [
+ {tag:'h2', html:"Direct logins"},
+ {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) {
+ return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[
+ {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']},
+// {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']}
+ {tag:'span', cls:'directLogin', html:aDirectLoginData['label']}
+ ]}
+ }, someData['directLogins'])}
+ ]);
+
+ MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) {
+ MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler');
+ }, this),
+ MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row'])
+ )
+ };
+
+ if (someData['notes'] != '') {
+ this.append(this.element(), [
+ {tag:'h2', html:"Notes"},
+ {tag:'fieldset', id:this.getId('fieldset'), children:[
+ {tag:'div', cls:'row notes', children:[
+ {tag:'span', html:someData['notes']}
+ ]}
+ ]}
+ ]);
+ };
+
+ return true;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'toggleClickHandler': function (anEvent) {
+ var nextState;
+ var fieldValue;
+
+//console.log("TOGGLE");
+ anEvent.preventDefault;
+ fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0];
+
+ nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true');
+ if (nextState) {
+ MochiKit.DOM.removeElementClass(fieldValue, 'clear');
+ } else {
+ MochiKit.DOM.addElementClass(fieldValue, 'clear');
+ }
+
+ MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState);
+ },
+* /
+ //=========================================================================
+/*
+ 'directLoginClickHandler': function (anEvent) {
+ anEvent.preventDefault();
+
+ if (/(directLogin_)/.test(anEvent.src().id)) {
+ var directLoginReference;
+
+ directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2];
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference});
+ }
+ },
+*/
+ //=========================================================================
+
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardList.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardList.js
index c3f2701..a4aa212 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardList.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/CardList.js
@@ -2,100 +2,153 @@
Copyright 2008-2011 Clipperz Srl
This file is part of Clipperz Community Edition.
Clipperz Community Edition is an online password manager.
For further information about its features and functionalities please
refer to http://www.clipperz.com.
* Clipperz Community Edition is free software: you can redistribute
it and/or modify it under the terms of the GNU Affero General Public
License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.
* Clipperz Community Edition is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
License along with Clipperz Community Edition. If not, see
<http://www.gnu.org/licenses/>.
*/
-Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components');
+Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
-Clipperz.PM.UI.iPhone.Components.CardList = function(args) {
+Clipperz.PM.UI.Mobile.Components.CardList = function(args) {
args = args || {};
- Clipperz.PM.UI.iPhone.Components.CardList.superclass.constructor.apply(this, arguments);
+ Clipperz.PM.UI.Mobile.Components.CardList.superclass.constructor.apply(this, arguments);
this._cardDetail = null;
return this;
}
//=============================================================================
-Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, {
+Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.CardList, Clipperz.PM.UI.Common.Components.BaseComponent, {
//-------------------------------------------------------------------------
'toString': function () {
- return "Clipperz.PM.UI.iPhone.Components.CardList component";
+ return "Clipperz.PM.UI.Mobile.Components.CardList component";
},
//-------------------------------------------------------------------------
- 'renderSelf': function(/*aContainer, aPosition*/) {
- this.append(this.element(), [
- {tag:'div', cls:'toolbar', id:'toolbar', children:[
- {tag:'h1', id:'pageTitle', html:"cards"},
- {tag:'a', id:'backButton', cls:'button', href:'#', html:"cards"}
+ 'renderSelf': function () {
+ this.append(this.element(), {tag:'div', cls:'cardList', children:[
+ {tag:'div', cls:'toolbar', children:[
+ {tag:'h1', html:"clipperz"},
+// {tag:'input', name:'search', type:'search', autocomplete:'off', placeholder:"search", id:this.getId('search')},
+ {tag:'a', href:'#', id:'settings', cls:'button', html:"*"}
]},
- {tag:'div', cls:'cardList', id:this.getId('cardList'), children:[
- {tag:'form', title:'search', cls:'panel cardListSearchForm', id:this.getId('cardListSearchForm'), children:[
- {tag:'input', type:'search', name:'search', value:"", placeholder:"search", id:this.getId('searchField')}
- ]},
- {tag:'ul', cls:'panel cardListPanel', id:this.getId('cardListPanel'), children:[]}
- ]},
- {tag:'div', cls:'panel cardDetailPanel', id:this.getId('cardDetail')}
- ]);
+ {tag:'div', cls:'scroll', id:this.getId('listBox'), children:[
+ {tag:'ul', cls:'rounded', id:this.getId('list'), children:[
+ {tag:'li', html:'loading'}
+ ]}
+ ]}
+ ]});
+
+ MochiKit.Signal.connect(this.getElement('list'), 'onclick', this, 'cardSelectionHandler');
+ MochiKit.Signal.connect(this.getElement('list'), 'ontouchstart', this, 'cardSelectionHandler');
+// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this, 'searchHandler');
+// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this, 'searchHandler');
+// MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this, 'searchHandler');
+
+// MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this, 'cardListClickHandler');
+// MochiKit.Signal.connect('backButton', 'onclick', this, 'backButtonClickHandler');
+
+// MochiKit.Style.hideElement('backButton');
+// MochiKit.Style.hideElement(this.getElement('cardDetail'));
+ },
- MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onsubmit', this, 'searchHandler');
- MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeydown', this, 'searchHandler');
- MochiKit.Signal.connect(this.getElement('cardListSearchForm'), 'onkeyup', this, 'searchHandler');
+ 'showCards': function (someCards) {
+ var cardListElement;
+ if (this.isFullyRendered() == false) {
+ this.render();
+ };
+
+ cardListElement = this.getElement('list')
+
+ cardInfo = {
+ '_rowObject': MochiKit.Async.succeed,
+ '_reference': MochiKit.Base.methodcaller('reference'),
+ '_searchableContent': MochiKit.Base.methodcaller('searchableContent'),
+ 'label': MochiKit.Base.methodcaller('label'),
+ 'favicon': MochiKit.Base.methodcaller('favicon')
+ };
+
+//console.log("someCards", someCards);
+ deferredResult = new Clipperz.Async.Deferred("CardList.showCards", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false}));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(MochiKit.Base.methodcaller('sort', Clipperz.Base.caseInsensitiveKeyComparator('label')));
+ deferredResult.addCallbackPass(MochiKit.DOM.replaceChildNodes, cardListElement);
+// deferredResult.addCallbackPass(MochiKit.DOM.removeElementClass, cardListElement, 'loading');
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'appendCardToList', cardListElement));
+ deferredResult.callback(someCards);
+ },
- MochiKit.Signal.connect(this.getElement('cardListPanel'), 'onclick', this, 'cardListClickHandler');
- MochiKit.Signal.connect('backButton', 'onclick', this, 'backButtonClickHandler');
+ 'appendCardToList': function (aCardListElement, aCardInfo) {
+//console.log("appendCardToList", aCardInfo);
+ this.append(aCardListElement, {tag:'li', cls:'cardListItem arrow', cardreference:aCardInfo['_reference'], children:[
+ {tag:'a', href:'#', html:aCardInfo['label'], children:[
+ {tag:'small', cls:'favicon', children:[{tag:'img', cls:'favicon', src:aCardInfo['favicon']}]}
+ ]}
+ ]});
+ },
+
+ 'cardSelectionHandler': function (anEvent) {
+ var listElement;
+ var cardReference;
- MochiKit.Style.hideElement('backButton');
- MochiKit.Style.hideElement(this.getElement('cardDetail'));
+ anEvent.preventDefault();
+
+ listElement = anEvent.target();
+ if (MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') == null) {
+ listElement = MochiKit.DOM.getFirstParentByTagAndClassName(anEvent.target(), tagName='li', className='cardListItem');
+ }
+ cardReference = MochiKit.DOM.getNodeAttribute(listElement, 'cardreference');
+console.log("###", listElement, cardReference);
+ // TODO: Notify card with reference MochiKit.DOM.getNodeAttribute(listElement, 'cardreference') has been selected
+ MochiKit.Signal.signal(this, 'selectedCard', cardReference);
},
//-------------------------------------------------------------------------
-
+/*
'searchHandler': function (anEvent) {
if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ENTER')) { // RETURN
anEvent.preventDefault();
} else {
if ((typeof(anEvent.key()) != 'undefined') && (anEvent.key().string == 'KEY_ESCAPE')) {
anEvent.target().value = "";
}
if (anEvent.type() == 'keyup') {
MochiKit.Signal.signal(this, 'searchEvent', anEvent.target().value);
}
}
},
//-------------------------------------------------------------------------
'update': function (someObjects) {
var cardListPanel;
var i,c;
cardListPanel = this.getElement('cardListPanel');
cardListPanel.innerHTML = '';
c = someObjects.length;
@@ -106,49 +159,49 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
{tag:'a', id:('cardListReference_' + someObjects[i]['_reference']), href:'#', html:someObjects[i]['label']}
]})
MochiKit.Signal.connect('cardListItem_' + someObjects[i]['_reference'], 'onclick', this, 'cardListClickHandler');
}
},
'cardListClickHandler': function (anEvent) {
anEvent.preventDefault();
if (/(cardListReference_|cardListItem_)/.test(anEvent.target().id)) {
var cardListReference;
cardListReference = anEvent.target().id.match(/(cardListReference_|cardListItem_)(.*)/)[2];
//console.log("Showing detail for card named", cardListReference);
MochiKit.Signal.signal(this, 'selectedCard', cardListReference);
}
},
//=========================================================================
'cardDetail': function (someData) {
if (this._cardDetail == null) {
- this._cardDetail = new Clipperz.PM.UI.iPhone.Components.CardDetail({element:this.getElement('cardDetail')});
+ this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:this.getElement('cardDetail')});
}
return this._cardDetail;
},
//-------------------------------------------------------------------------
'removeCardDetail': function () {
if (this._cardDetail != null) {
this._cardDetail.remove();
this._cardDetail = null;
}
},
//=========================================================================
'showCard': function (someData) {
var deferredResult;
var offset;
offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
this.cardDetail().render();
this.cardDetail().setCardReference(someData['_reference']);
MochiKit.Style.setElementPosition(this.cardDetail().element(), {x:offset});
@@ -174,28 +227,28 @@ Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardList, Clipperz.PM.UI.C
return this.cardDetail().showCardDetails(someData);
},
//=========================================================================
'backButtonClickHandler': function (anEvent) {
var offset;
anEvent.preventDefault();
MochiKit.DOM.getElement('pageTitle').innerHTML = "cards";
offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
MochiKit.Style.setElementPosition(this.getElement('cardList'), {x:-offset});
MochiKit.DOM.showElement(this.getElement('cardList'));
new MochiKit.Visual.Parallel([
new MochiKit.Visual.Move(this.getElement('cardList'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}),
new MochiKit.Visual.Move(this.getElement('cardDetail'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}),
MochiKit.Visual.fade (this.getElement('cardDetail'), { transition:MochiKit.Visual.Transitions.linear, sync:true}),
MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true})
], {duration:1, afterFinish:MochiKit.Base.method(this, 'removeCardDetail')})
},
-
+*/
//=========================================================================
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js
new file mode 100644
index 0000000..eafcdbc
--- a/dev/null
+++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Components/LoginForm.js
@@ -0,0 +1,356 @@
+/*
+
+Copyright 2008-2011 Clipperz Srl
+
+This file is part of Clipperz Community Edition.
+Clipperz Community Edition is an online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz Community Edition is free software: you can redistribute
+ it and/or modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation, either version
+ 3 of the License, or (at your option) any later version.
+
+* Clipperz Community Edition is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz Community Edition. If not, see
+ <http://www.gnu.org/licenses/>.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI.Mobile.Components');
+
+Clipperz.PM.UI.Mobile.Components.LoginForm = function(args) {
+ args = args || {};
+
+ this._pin = '';
+
+ this._message = null;
+ this._steps = 0;
+ this._actualSteps = 0;
+
+ this._callback = null;
+ this._errorCallback = null;
+
+ this._mode = 'CREDENTIALS';
+
+ Clipperz.PM.UI.Mobile.Components.LoginForm.superclass.constructor.apply(this, arguments);
+
+ return this;
+}
+
+//=============================================================================
+
+Clipperz.Base.extend(Clipperz.PM.UI.Mobile.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, {
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return "Clipperz.PM.UI.Mobile.Components.LoginForm component";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'callback': function () {
+ return this._callback;
+ },
+
+ 'errorCallback': function () {
+ return this._errorCallback;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'mode': function () {
+ return this._mode;
+ },
+
+ 'setMode': function (aValue) {
+ this._mode = aValue;
+ },
+
+ //..........................................................................
+
+ 'pin': function () {
+ return this._pin;
+ },
+
+ 'setPin': function (aValue) {
+ this._pin = aValue;
+ },
+
+ //..........................................................................
+
+ 'username': function () {
+ return this._username;
+ },
+
+ 'setUsername': function (aValue) {
+ this._username = aValue;
+ },
+
+ //..........................................................................
+
+ 'passphrase': function () {
+ return this._passphrase;
+ },
+
+ 'setPassphrase': function (aValue) {
+ this._passphrase = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'message': function () {
+ return this._message;
+ },
+
+ '_setMessage': function (aValue) {
+ this._message = aValue;
+
+ if (aValue == null) {
+ MochiKit.Style.hideElement(this.getElement('credentialsMessage'));
+ } else {
+ this.getElement('message').innerHTML = aValue;
+ MochiKit.Style.showElement(this.getElement('credentialsMessage'));
+ }
+ },
+
+ 'setMessage': function (aValue) {
+ this._setMessage(aValue);
+ MochiKit.DOM.removeElementClass(this.getElement('credentialsMessage'), 'error');
+ },
+
+ 'setErrorMessage': function (aValue) {
+ this._setMessage(aValue);
+ MochiKit.DOM.addElementClass(this.getElement('credentialsMessage'), 'error');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setCallbacks': function (args) {
+ this._callback = args['callback'];
+ this._errorCallback = args['errorCallback'];
+ },
+
+ 'showErrors': function (args) {
+//console.log("LoginForm.showErrors", args);
+ if (args['previousFailedAttempt'] == 'LOGIN') {
+ this.setErrorMessage("Wrong credentials");
+ } else if (args['previousFailedAttempt'] == 'PIN') {
+ if (args['failedAttempts'] == -1) {
+ this.setErrorMessage("Wrong PIN - Resetted");
+ } else {
+ this.setErrorMessage("Wrong PIN");
+ }
+ } else {
+ this.setMessage(null);
+ }
+ },
+
+ 'updateWithArgs': function (args) {
+ this.renderIfNeeded();
+ this.setCallbacks(args);
+ this.showErrors(args);
+ this.updateRendering();
+ },
+
+ 'showPinLogin': function (args) {
+ this.setPin('');
+ this.setMode('PIN');
+ this.updateWithArgs(args);
+
+// $(this.getAnchor('PIN')).focus();
+ this.getElement('PIN').focus();
+ },
+
+ 'showCredentialsLogin': function (args) {
+ this.setMode('CREDENTIALS');
+ this.updateWithArgs(args);
+
+ if (this.getElement('usernameField').value.length == 0) {
+// $(this.getAnchor('usernameField')).focus();
+ this.getElement('usernameField').focus();
+ } else {
+// $(this.getAnchor('passphraseField')).focus();
+ this.getElement('passphraseField').focus();
+ this.getElement('passphraseField').select();
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'renderIfNeeded': function () {
+ if (this.isFullyRendered() == false) {
+ this.render();
+ };
+ this.updateRendering();
+ },
+
+ 'updateRendering': function () {
+ MochiKit.Style.showElement(this.getElement('credentialsBody'));
+ MochiKit.Style.hideElement(this.getElement('validating'));
+
+// this.hideAllPanes();
+ MochiKit.Base.map(function (aNode) { MochiKit.Style.hideElement(aNode); }, MochiKit.Selector.findDocElements('div.credentialsBody > div'));
+ if (this.mode() == 'CREDENTIALS') {
+ selectedPanel = this.getElement('credentials')
+ } else if (this.mode() == 'PIN') {
+ selectedPanel = this.getElement('pin')
+// this.updatePinDisplay();
+ } else {
+ throw 'Unhandled login form mode';
+ }
+ MochiKit.Style.showElement(selectedPanel);
+
+ MochiKit.Style.hideElement(this.getElement('validating'));
+ },
+
+ 'renderSelf': function() {
+ var selectedPanel;
+ this.append(this.element(), {tag:'div', id:'login', children:[
+ {tag:'div', cls:'toolbar', children:[
+ {tag:'h1', html:"clipperz"}
+ ]},
+ {tag:'div', cls:'scroll', children:[
+ //==================================================================
+ {tag:'div', cls:'credentialsMessage', id:this.getId('credentialsMessage'), children:[
+ {tag:'h1', cls:'message', id:this.getId('message'), html:"Message"}
+ ]},
+ //==================================================================
+ {tag:'div', cls:'credentialsBody', id:this.getId('credentialsBody'), children:[
+ //--------------------------------------------------------------
+ {tag:'div', cls:'pin', id:this.getId('pin'), children:[
+ {tag:'form', cls:'scroll', id:this.getId('pinForm'), children:[
+ {tag:'ul', cls:'edit rounded', children:[
+ {tag:'li', children:[{tag:'input', type:'number', name:'PIN', placeholder:"PIN", id:this.getId('PIN') }]},
+ ]},
+ {tag:'a', href:'#', cls:'greenButton', id:this.getId('pinSubmitButton'), html:"Login"}
+ ]}
+ ]},
+ //--------------------------------------------------------------
+ {tag:'div', cls:'credentials', id:this.getId('credentials'), children:[
+ {tag:'form', cls:'scroll', id:this.getId('credentialsForm'), children:[
+ {tag:'ul', cls:'edit rounded', children:[
+ {tag:'li', children:[{tag:'input', type:'email', name:'name', /*value:'joe',*/ placeholder:"username", id:this.getId('usernameField') }]},
+ {tag:'li', children:[{tag:'input', type:'password', name:'passphrase', /*value:'clipperz',*/ placeholder:"passphrase", id:this.getId('passphraseField') }]}
+ ]},
+ {tag:'a', href:'#', cls:'greenButton', id:this.getId('credentialsSubmitButton'), html:"Login"}
+// {tag:'input', type:'submit', cls:'greenButton', id:this.getId('credentialsSubmitButton'), value:"Login"}
+
+ ]}
+ ]},
+ //--------------------------------------------------------------
+ ]},
+ //==================================================================
+ {tag:'div', cls:'validating', id:this.getId('validating'), children:[
+ {tag:'div', cls:'loading', children:[
+ {tag:'div', cls:'spinner', children:[
+ {tag:'div', cls:'bar01'},
+ {tag:'div', cls:'bar02'},
+ {tag:'div', cls:'bar03'},
+ {tag:'div', cls:'bar04'},
+ {tag:'div', cls:'bar05'},
+ {tag:'div', cls:'bar06'},
+ {tag:'div', cls:'bar07'},
+ {tag:'div', cls:'bar08'},
+ {tag:'div', cls:'bar09'},
+ {tag:'div', cls:'bar10'},
+ {tag:'div', cls:'bar11'},
+ {tag:'div', cls:'bar12'}
+ ]}
+ ]},
+ {tag:'div', id:this.getId('loadingMessage')},
+ {tag:'a', href:'#', cls:'grayButton', id:this.getId('loginCancelButton'), html:"Cancel"}
+ ]}
+ //==================================================================
+ ]}
+ ]});
+
+ MochiKit.Signal.connect(this.getElement('credentialsForm'), 'onsubmit', this, 'submitCredentialsHandler');
+ MochiKit.Signal.connect(this.getElement('credentialsSubmitButton'), 'onclick', this, 'submitCredentialsHandler');
+
+ MochiKit.Signal.connect(this.getElement('pinForm'), 'onsubmit', this, 'submitPinHandler');
+ MochiKit.Signal.connect(this.getElement('pinSubmitButton'), 'onclick', this, 'submitPinHandler');
+
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'initProgress', this, 'initProgressHandle');
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'updateProgress', this, 'updateProgressHandle');
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'advanceProgress', this, 'advanceProgressHandle');
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'progressDone', this, 'progressDoneHandle');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'submitPinHandler': function (anEvent) {
+ var pin;
+
+ this.setMessage(null);
+ pin = this.getElement('PIN').value;
+// $(this.getAnchor('PIN')).blur();
+ this.getElement('PIN').blur();
+
+ credentials = Clipperz.PM.PIN.credentialsWithPIN(pin);
+ this.loginWithCredentials(credentials);
+ },
+
+ 'submitCredentialsHandler': function (anEvent) {
+//console.log("submitCredentialsHandler");
+ var credentials;
+
+ this.setMessage(null);
+
+ credentials = {};
+ credentials['username'] = this.getElement('usernameField').value;
+ credentials['passphrase'] = this.getElement('passphraseField').value;
+// $(this.getAnchor('passphraseField')).blur();
+ this.getElement('passphraseField').blur();
+
+ this.loginWithCredentials(credentials);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'loginWithCredentials': function (someCredentials) {
+ var args;
+
+ args = {};
+ args['credentials'] = someCredentials;
+ args['errorCallback'] = this.errorCallback();
+
+ MochiKit.Style.hideElement(this.getElement('credentialsBody'));
+ MochiKit.Style.showElement(this.getElement('validating'));
+
+ MochiKit.Async.callLater(0.1, this.callback(), args);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'initProgressHandle': function (anEvent) {
+//console.log("** initProgressHandle", anEvent);
+ this._steps = anEvent['steps'];
+ this._actualSteps = 0;
+ },
+
+ 'updateProgressHandle': function (anEvent) {
+//console.log("** updateProgressHandle", anEvent);
+ this._steps += anEvent['extraSteps'];
+ },
+
+ 'advanceProgressHandle': function (anEvent) {
+//console.log("** advanceProgressHandle", anEvent);
+ this._actualSteps ++;
+//console.log("STEPS: " + this._actualSteps + "/" + this._steps);
+ },
+
+ 'progressDoneHandle': function (anEvent) {
+//console.log("** progressDoneHandle", anEvent);
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js
new file mode 100644
index 0000000..12a61f7
--- a/dev/null
+++ b/frontend/gamma/js/Clipperz/PM/UI/Mobile/Controllers/MainController.js
@@ -0,0 +1,393 @@
+/*
+
+Copyright 2008-2011 Clipperz Srl
+
+This file is part of Clipperz Community Edition.
+Clipperz Community Edition is an online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz Community Edition is free software: you can redistribute
+ it and/or modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation, either version
+ 3 of the License, or (at your option) any later version.
+
+* Clipperz Community Edition is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz Community Edition. If not, see
+ <http://www.gnu.org/licenses/>.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI.Mobile.Controllers');
+
+Clipperz.PM.UI.Mobile.Controllers.MainController = function() {
+ this._jQTouch = null;
+ this._user = null;
+ this._proxy = null;
+ this._loginForm = null;
+ this._cardList = null;
+ this._cardDetail = null;
+
+ return this;
+}
+
+MochiKit.Base.update(Clipperz.PM.UI.Mobile.Controllers.MainController.prototype, {
+
+ 'toString': function () {
+ return "Clipperz.PM.UI.Mobile.Controllers.MainController";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'user': function () {
+ return this._user;
+ },
+
+ 'setUser': function (aValue) {
+ this._user = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'jQTouch': function () {
+ return this._jQTouch;
+ },
+
+ 'setJQTouch': function (aValue) {
+ this._jQTouch = aValue;
+ },
+
+ //=========================================================================
+
+ 'run': function () {
+ console.log("MainController.run");
+
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'doLogin', MochiKit.Base.method(this, 'doLogin'));
+ Clipperz.DOM.Helper.overwrite(MochiKit.DOM.currentDocument().body, {tag:'div', id:'jqt', children:[
+ {tag:'div', id:'loginForm'},
+ {tag:'div', id:'cardList'},
+ {tag:'div', id:'cardDetail'},
+ {tag:'div', id:'preferences'}
+ ]});
+
+ this.showLoginForm();
+
+ this.initjQTouch();
+
+
+// this.showAddToHomeScreenBaloon();
+// this.selectInitialProxy();
+ },
+
+ 'initjQTouch': function () {
+ var jqt;
+
+ jqt = new $.jQTouch({
+ icon: 'data:image/png;charset=utf-8;base64,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',
+// icon4: 'jqtouch4.png',
+// startupScreen: null, // Pass a string path to a 320px x 460px startup screen for full screen apps.
+ statusBar: 'black-translucent', // Styles the status bar when running as a fullscreen app. Other options are `default`, `black`, and `black-translucent`.
+// addGlossToIcon: true, // Set to 'false' to prevent automatic glossy button effect on icon.
+ preloadImages: false, // Pass an array of image paths to load them before page loads. Ex: `['images/link_over.png', 'images/link_select.png']`
+ fixedViewport: true, // Removes the user's ability to scale the page. Ensures the site behaves more like an application.
+// fullScreen: true, // The website will become a fullscreen application when saved to a user's home screen. Set to `false` to disable.
+// fullScreenClass: 'fullscreen' // Adds a class to the `<body>` when running in full-screen mode, to allow for easy detection and styling. Set to `false` to disable.
+// themeSelectionSelector: '#jqt #themes ul', // ???
+
+// useAnimations: true, // Set to `false` to disable all animations.
+// useFastTouch: true, // Removes ~350ms onClick delay when tapping a link (use in conjunction with the .tap() event) **Experimental**
+// useTouchScroll: true, // Adds support for iOS5 scrolling. Set to false to disable. **Experimental**
+
+ cacheGetRequests: false, // Automatically caches GET requests, so subsequent taps reference the pre-loaded views. (default: true)
+
+// backSelector: '.back, .cancel, .goback', // A CSS selector for back links/buttons. When clicked, the page history goes back one, automatically reversing whichever entrance animation was used.
+
+// cubeSelector: '.cube', // Link selector for a cube animation.
+// dissolveSelector: '.dissolve', // Link selector for a dissolve animation.
+// fadeSelector: '.fade', // Link selector for a fade animation.
+// flipSelector: '.flip', // Link selector for a 3d flip animation.
+ formSelector: null, // Sets which forms are automatically submitted via Ajax. (default: 'form')
+// popSelector: '.pop', // Link selector for a pop animation. (default: '.pop')
+// slideSelector: 'body > * > ul li a', // Link selector for the default slide-left transition. By default applies to all links within an unordered list. Accepts any jQuery-capable selector `'li &gt; a, a:not(.dontslide)'`, etc. (default: 'body > * > ul li a')
+// slideupSelector: '.slideup', // Link selector for a slide up animation. (default: '.slideup')
+// submitSelector: '.submit', // Selector which, when clicked, will submit its parent form (and close keyboard if open). (default: '.submit')
+// swapSelector: '.swap', // Link selector for 3d swap animation. (default: '.swap')
+// touchSelector: 'a, .touch', // Selector for items which are automatically given expanded touch events. This makes ordinary links more responsive and provides trigger events like `swipe` (default: 'a, .touch')
+
+ debug: false
+ });
+
+ this.setJQTouch(jqt);
+ },
+
+ //=========================================================================
+
+ 'showAddToHomeScreenBaloon': function () {
+console.log(">>> showAddToHomeScreenBaloon");
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'selectInitialProxy': function () {
+//console.log(">>> selectInitialProxy");
+ if (this.isOnline()) {
+//console.log("--- selectInitialProxy: using default proxy");
+ this._proxy = Clipperz.PM.Proxy.defaultProxy;
+ } else {
+ if (this.hasLocalData()) {
+//console.log("--- selectInitialProxy: using local cache proxy");
+ this._proxy = new Clipperz.PM.Proxy.OfflineCache({'shouldPayTolls':false});
+ } else {
+ this.showOfflineError();
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'showLoginForm': function (args) {
+ args = args || {};
+
+ args['callback'] = MochiKit.Base.method(this, 'doLogin');
+
+ if (Clipperz.PM.PIN.isSet()) {
+ args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedPinLogin');
+ this.loginForm().showPinLogin(args);
+ } else {
+ args['errorCallback'] = MochiKit.Base.method(this, 'handleFailedCredentialsLogin');
+ this.loginForm().showCredentialsLogin(args);
+ }
+ },
+
+ //.........................................................................
+
+ 'handleFailedCredentialsLogin': function () {
+console.log("LOGIN FAILED");
+ this.showLoginForm({'previousFailedAttempt':'LOGIN'});
+ },
+
+ //.........................................................................
+
+ 'handleFailedPinLogin': function () {
+ var failedAttempts;
+ var status;
+
+ failedAttempts = Clipperz.PM.PIN.recordFailedAttempt();
+ this.showLoginForm({'previousFailedAttempt':'PIN', 'failedAttempts': failedAttempts});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'doLogin': function (someArgs) {
+ var deferredResult;
+ var credentials;
+ var errorCallback;
+ var user;
+ var getPassphraseDelegate;
+
+//console.log(">>> MainController.doLogin", someArgs);
+ credentials = someArgs['credentials'];
+ errorCallback = someArgs['errorCallback'] || MochiKit.Base.noop;
+
+ getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase);
+ user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate});
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false});
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4});
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
+ deferredResult.addMethod(user, 'login');
+ deferredResult.addCallbacks(
+ MochiKit.Base.method(this, 'processSuccessfulLogin', user),
+ errorCallback
+ );
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //..........................................................................
+
+ 'processSuccessfulLogin': function (aUser) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.processSuccessfulLogin', {trace:false});
+ deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount');
+// deferredResult.addMethod(this, 'removeLoginForm');
+ deferredResult.addMethod(this, 'setUser', aUser);
+ deferredResult.addMethod(this, 'setupApplication');
+ deferredResult.addMethod(this, 'runApplication');
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupApplication': function () {
+ var deferredResult;
+
+console.log(">>> setupApplication");
+ deferredResult = new Clipperz.Async.Deferred("MainController.setupApplication", {trace:false});
+ deferredResult.addMethod(this, 'welcomeFirstTimeUser');
+ deferredResult.addMethod(this, 'showPaymentReminder');
+ deferredResult.addMethod(this, 'copyDataLocally');
+ deferredResult.callback(arguments);
+
+ return deferredResult;
+ },
+
+
+ //..........................................................................
+
+ 'isFirstTimeUser': function () {
+ return false;
+ },
+
+ 'welcomeFirstTimeUser': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.welcomeFirstTimeUser', {trace:false});
+
+ if (this.isFirstTimeUser()) {
+ deferredResult.addCallback(function () { console.log("--> welcome"); });
+ }
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //..........................................................................
+
+ 'shouldShowPaymentReminder': function () {
+ return true;
+ },
+
+ 'showPaymentReminder': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.showPaymentReminder', {trace:false});
+
+ if (this.shouldShowPaymentReminder()) {
+ deferredResult.addCallback(function () { console.log("--> payment reminder"); });
+ }
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //..........................................................................
+
+ 'canCopyDataLocally': function () {
+ return false;
+ },
+
+ 'copyDataLocally': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.copyDataLocally', {trace:false});
+
+ if (this.canCopyDataLocally()) {
+ deferredResult.addCallback(function () { console.log("--> copy data locally"); });
+ }
+ deferredResult.callback();
+
+ return deferredResult;
+
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'runApplication': function () {
+ var deferredResult;
+
+//console.log(">>> runApplication");
+ deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:true});
+ deferredResult.addMethod(this.user(), 'getRecords');
+ deferredResult.addMethod(this, 'showCards');
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'showOfflineError': function (anException) {
+ alert("Error: " + anException);
+ throw anException;
+ },
+
+ //=========================================================================
+
+ 'isOnline': function() {
+ return navigator.onLine;
+ },
+
+ 'hasLocalData': function() {
+ return false;
+ },
+
+ //=========================================================================
+
+ 'loginForm': function() {
+ if (this._loginForm == null) {
+ this._loginForm = new Clipperz.PM.UI.Mobile.Components.LoginForm({element:MochiKit.DOM.getElement('loginForm')});
+ }
+
+ return this._loginForm;
+ },
+
+ 'removeLoginForm': function () {
+ if (this._loginForm != null) {
+ this._loginForm.remove();
+ this._loginForm = null;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'cardList': function () {
+ if (this._cardList == null) {
+ this._cardList = new Clipperz.PM.UI.Mobile.Components.CardList({element:MochiKit.DOM.getElement('cardList')});
+ MochiKit.Signal.connect(this._cardList, 'selectedCard', this, 'selectCardHandler');
+ }
+
+ return this._cardList;
+ },
+
+ 'showCards': function (someCards) {
+ this.cardList().showCards(someCards);
+ this.jQTouch().goTo('#cardList', 'slideleft');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'cardDetail': function () {
+ if (this._cardDetail == null) {
+ this._cardDetail = new Clipperz.PM.UI.Mobile.Components.CardDetail({element:MochiKit.DOM.getElement('cardDetail')});
+ }
+
+ return this._cardDetail;
+ },
+
+ 'selectCardHandler': function (aCardReference) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("MainController.selectCardHandler", {trace:true});
+ deferredResult.addMethod(this.cardDetail(), 'render');
+ deferredResult.addMethod(this.jQTouch(), 'goTo', '#cardDetail', 'slideleft');
+ deferredResult.addMethod(this.user(), 'getRecord', aCardReference);
+ deferredResult.addMethod(this.cardDetail(), 'showCard');
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js
index ee6d7a3..d6b0574 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/AccountPanel.js
@@ -20,126 +20,131 @@ refer to http://www.clipperz.com.
* You should have received a copy of the GNU Affero General Public
License along with Clipperz Community Edition. If not, see
<http://www.gnu.org/licenses/>.
*/
Clipperz.Base.module('Clipperz.PM.UI.Web.Components');
Clipperz.PM.UI.Web.Components.AccountPanel = function(args) {
args = args || {};
Clipperz.PM.UI.Web.Components.AccountPanel.superclass.constructor.apply(this, arguments);
// this._initiallySelectedTab = args.selected || 'ACCOUNT';
this._initiallySelectedTab = args.selected || 'PASSPHRASE';
this._tabPanelControllerConfiguration = {
// 'ACCOUNT': {
// tab: 'accountTab',
// panel: 'accountPanel'
// },
'PASSPHRASE': {
tab: 'passphraseTab',
panel: 'passphrasePanel'
},
+ 'OTP': {
+ tab: 'OTPTab',
+ panel: 'OTPPanel'
+ },
'PREFERENCES': {
tab: 'preferencesTab',
panel: 'preferencesPanel'
},
'LOGIN_HISTORY': {
tab: 'loginHistoryTab',
panel: 'loginHistoryPanel'
}
};
return this;
}
//=============================================================================
Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.AccountPanel, Clipperz.PM.UI.Common.Components.TabPanelComponent, {
//-------------------------------------------------------------------------
'toString': function () {
return "Clipperz.PM.UI.Web.Components.AccountPanel component";
},
//-------------------------------------------------------------------------
'renderSelf': function(/*aContainer, aPosition*/) {
//Clipperz.log("AccountPanel.renderSelf element", this.element());
this.append(this.element(), [
{tag:'div', cls:'header', children:[
{tag:'div', cls:'subPanelTabs', children:[
{tag:'ul', children:[
// {tag:'li', id:this.getId('accountTab'), children:[{tag:'a', href:'#', html:'Account'}], cls:'first'},
{tag:'li', id:this.getId('passphraseTab'), children:[{tag:'a', href:'#', html:'Passphrase'}], cls:'first'},
+ {tag:'li', id:this.getId('OTPTab'), children:[{tag:'a', href:'#', html:'One Time Passwords'}]},
{tag:'li', id:this.getId('preferencesTab'), children:[{tag:'a', href:'#', html:'Preferences'}]},
{tag:'li', id:this.getId('loginHistoryTab'), children:[{tag:'a', href:'#', html:'Login history'}]}
]}
]}
]},
{tag:'div', cls:'body', children:[
{tag:'div', cls:'accountPanel', children:[
{tag:'div', cls:'subPanelContent', children:[
{tag:'ul', children:[
// {tag:'li', id:this.getId('accountPanel'), children:[
// {tag:'h3', html:"-- Account --"}
// ]},
{tag:'li', id:this.getId('passphrasePanel'), children:[
{tag:'h3', cls:'changePassphrase', html:"Change passphrase"},
{tag:'form', id:this.getId('changePassphrase'), cls:'changePassphrase', children:[
{tag:'div', cls:'currentCredentials', children:[
{tag:'div', cls:'field username', children:[
{tag:'label', html:"username", 'for':this.getId('currentUsername')},
{tag:'input', id:this.getId('currentUsername')}
]},
{tag:'div', cls:'field passphrase', children:[
{tag:'label', html:"passphrase", 'for':this.getId('currentPassphrase')},
{tag:'input', id:this.getId('currentPassphrase')}
]}
]},
{tag:'div', cls:'newPassphrase', children:[
{tag:'div', cls:'field', children:[
{tag:'label', html:"new passphrase", 'for':this.getId('newPassphrase')},
{tag:'input', id:this.getId('newPassphrase')}
]},
{tag:'div', cls:'field', children:[
{tag:'label', html:"re-new passphrase", 'for':this.getId('reNewPassphrase')},
{tag:'input', id:this.getId('reNewPassphrase')}
]}
]},
{tag:'div', cls:'confirm', children:[
{tag:'input', type:'checkbox', id:this.getId('confirm')},
{tag:'label', html:"I understand that Clipperz will not be able to recover a lost passphrase", 'for':this.getId('confirm')}
]}
]},
{tag:'div', cls:'clear'},
{tag:'div', cls:'confirmButton', id:this.getId('confirmationButton'), children:[
{tag:'span', html:"change passphrase"}
+ ]}
]},
-
- {tag:'h3', cls:'manageOTP', html:"Manage One-Time Passphrases"},
- {}
+ {tag:'li', id:this.getId('OTPPanel'), children:[
+// {tag:'h3', html:"Manage One-Time Passphrases"}
]},
{tag:'li', id:this.getId('preferencesPanel'), children:[
- {tag:'h3', html:"-- Preferences --"}
+// {tag:'h3', html:"-- Preferences --"}
]},
{tag:'li', id:this.getId('loginHistoryPanel'), children:[
- {tag:'h3', html:"-- Login History --"}
+// {tag:'h3', html:"-- Login History --"}
]}
]}
]}
]}
]},
{tag:'div', cls:'footer'}
]);
this.tabPanelController().setup({selected:this.initiallySelectedTab()});
},
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js
index d2f1045..462d864 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/DataPanel.js
@@ -61,48 +61,56 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.DataPanel, Clipperz.PM.UI.Com
'toString': function () {
return "Clipperz.PM.UI.Web.Components.DataPanel component";
},
//-------------------------------------------------------------------------
'renderSelf': function(/*aContainer, aPosition*/) {
this.append(this.element(), [
{tag:'div', cls:'header', children:[
{tag:'div', cls:'subPanelTabs', children:[
{tag:'ul', children:[
{tag:'li', id:this.getId('offlineCopyTab'), children:[{tag:'a', href:'#', html:'Offline copy'}], cls:'first'},
{tag:'li', id:this.getId('sharingTab'), children:[{tag:'a', href:'#', html:'Sharing'}]},
{tag:'li', id:this.getId('importTab'), children:[{tag:'a', href:'#', html:'Import'}]},
{tag:'li', id:this.getId('exportTab'), children:[{tag:'a', href:'#', html:'Export'}]}
]}
]}
]},
{tag:'div', cls:'body', children:[
{tag:'div', cls:'accountPanel', children:[
{tag:'div', cls:'subPanelContent', children:[
{tag:'ul', children:[
{tag:'li', id:this.getId('offlineCopyPanel'), children:[
- {tag:'h3', html:"Offline copy"}
+// {tag:'h3', html:"Offline copy"},
+ {tag:'p', html:"With just one click you can dump all your encrypted data from Clipperz servers to your hard disk and create a read-only offline version of Clipperz to be used when you are not connected to the Internet."},
+ {tag:'a', id:this.getId('offlineCopyDownloadLink'), href:'#', html:"Download", cls:'downloadOfflineCopy'}
]},
{tag:'li', id:this.getId('sharingPanel'), children:[
- {tag:'h3', html:"Sharing"}
+// {tag:'h3', html:"Sharing"}
]},
{tag:'li', id:this.getId('importPanel'), children:[
- {tag:'h3', html:"Import"}
+// {tag:'h3', html:"Import"}
]},
{tag:'li', id:this.getId('exportPanel'), children:[
- {tag:'h3', html:"Export"}
+// {tag:'h3', html:"Export"}
]}
]}
]}
]}
]},
{tag:'div', cls:'footer'}
]);
this.tabPanelController().setup({selected:this.initiallySelectedTab()});
+ MochiKit.Signal.connect(this.getId('offlineCopyDownloadLink'), 'onclick', this, 'downloadOfflineCopy');
+ },
+
+ 'downloadOfflineCopy': function (anEvent) {
+ anEvent.preventDefault();
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', anEvent.src());
},
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js
index 5d082b5..26506e7 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/LoginProgress.js
@@ -61,88 +61,92 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.LoginProgress, Clipperz.PM.UI
// var loginProgressElement;
//
// loginProgressElement = MochiKit.DOM.getElement('loginProgress');
//
// if (loginProgressElement == null) {
// loginProgressElement = this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress'}, true);
// }
//console.log(">> LoginProgress.renderSelf", this.element());
this.append(this.element(), {tag:'div', id:'loginProgress', cls:'LoginProgress', children: [
// this.append(loginProgressElement, [
{tag:'div', cls:'header', children:[
{tag:'h3', id:this.getId('title'), html:"login progress"}
]},
{tag:'div', cls:'body', children:[
{tag:'div', id:this.getId('progressBar')},
{tag:'div', id:this.getId('errorBox'), cls:'errorBox', children:[
// {tag:'div', cls:'img ALERT', children:[{tag:'div'}]},
{tag:'div', cls:'img ALERT', children:[{tag:'canvas', id:this.getId('canvas')}]},
{tag:'p', html:"Login failed"}
]}
]},
{tag:'div', cls:'footer', children:[
{tag:'div', cls:'buttonArea', id:this.getId('buttonArea'), children:[
- {tag:'div', cls:'button', id:this.getId('button'), children:[
- {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"}
- ]}
+// {tag:'div', cls:'button', id:this.getId('button'), children:[
+// {tag:'a', href:'#', id:this.getId('buttonLink'), html:"cancel"}
+// ]}
+ {tag:'a', cls:'button', id:this.getId('button'), html:"cancel"}
]}
]}
]});
// ]);
Clipperz.PM.UI.Canvas.marks['!'](this.getElement('canvas'), "#ffffff");
this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
MochiKit.Style.hideElement(this.getElement('errorBox'));
- MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler');
+// MochiKit.Signal.connect(this.getId('buttonLink'), 'onclick', this, 'cancelEventHandler');
+ MochiKit.Signal.connect(this.getId('button'), 'onclick', this, 'cancelEventHandler');
},
//-------------------------------------------------------------------------
'displayElement': function() {
return MochiKit.DOM.getElement('loginProgress');
},
//-------------------------------------------------------------------------
'cancelEventHandler': function(anEvent) {
anEvent.preventDefault();
MochiKit.Signal.signal(this, 'cancelEvent');
},
//-------------------------------------------------------------------------
'disableCancel': function() {
MochiKit.Style.hideElement(this.getElement('buttonArea'));
},
//-------------------------------------------------------------------------
'showErrorMessage': function() {
- this.getElement('buttonLink').innerHTML = "close";
+// this.getElement('buttonLink').innerHTML = "close";
+ this.getElement('button').innerHTML = "close";
+ MochiKit.DOM.addElementClass(this.getElement('button'), 'default');
MochiKit.Style.hideElement(this.getElement('progressBar'));
this.getElement('title').innerHTML = "Error";
MochiKit.Style.showElement(this.getElement('errorBox'));
MochiKit.Style.showElement(this.getElement('buttonArea'));
},
//-------------------------------------------------------------------------
'deferredHideModalAndRemove': function(someParameters, aResult) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("LoginProgress.deferredHideModalAndRemove", {trace:false});
deferredResult.addMethod(this, 'deferredHideModal');
deferredResult.addMethod(this, 'remove');
deferredResult.addCallback(function () {
return aResult;
});
deferredResult.callback(someParameters);
return deferredResult;
},
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
@@ -28,41 +28,40 @@ Clipperz.Base.module('Clipperz.PM.UI.Web.Components');
Clipperz.PM.UI.Web.Components.PageFooter = function(args) {
args = args || {};
Clipperz.PM.UI.Web.Components.PageFooter.superclass.constructor.apply(this, arguments);
return this;
}
//=============================================================================
Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.PageFooter, Clipperz.PM.UI.Common.Components.BaseComponent, {
//-------------------------------------------------------------------------
'toString': function () {
return "Clipperz.PM.UI.Web.Components.PageFooter component";
},
//-------------------------------------------------------------------------
'renderSelf': function(/*aContainer, aPosition*/) {
this.append(this.element(), [
{tag:'div', cls:'footerWrapper', children:[
{tag:'div', cls:'footerContent', children:[
-// {tag:'div', cls:'footerStarIcon'},
{tag:'canvas', id:this.getId('footerStarIcon'), cls:'footerStarIcon'},
- {tag:'span', cls:'copyright', html:'Copyright &copy; 2009-2012 Clipperz Srl'},
+ {tag:'span', cls:'copyright', html:'Copyright &copy; 2009-2013 Clipperz Srl'},
{tag:'a', href:'http://www.clipperz.com/terms_of_service', target:'_blank', html:'terms of service'},
{tag:'a', href:'http://www.clipperz.com/privacy_policy', target:'_blank', html:'privacy policy'},
- {tag:'span', cls:'applicationVersion', html:'application version: [1992]'}
+ {tag:'div', cls:'applicationVersion', htmlString:'application version: <a href="https://github.com/clipperz/password-manager/tree/' + Clipperz_version + '" target="github">' + Clipperz_version + '</a>'}
]}
]}
]);
Clipperz.PM.UI.Canvas.star.normal(this.getElement('footerStarIcon'), "#7e7e7e");
},
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js
index 3ee6189..0fa369f 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Components/ToolsPanel.js
@@ -61,50 +61,50 @@ Clipperz.Base.extend(Clipperz.PM.UI.Web.Components.ToolsPanel, Clipperz.PM.UI.Co
'toString': function () {
return "Clipperz.PM.UI.Web.Components.ToolsPanel component";
},
//-------------------------------------------------------------------------
'renderSelf': function(/*aContainer, aPosition*/) {
this.append(this.element(), [
{tag:'div', cls:'header', children:[
{tag:'div', cls:'subPanelTabs', children:[
{tag:'ul', children:[
{tag:'li', id:this.getId('passwordGeneratorTab'), children:[{tag:'a', href:'#', html:'Password generator'}], cls:'first'},
{tag:'li', id:this.getId('bookmarkletTab'), children:[{tag:'a', href:'#', html:'Bookmarklet'}]},
{tag:'li', id:this.getId('compactEditionTab'), children:[{tag:'a', href:'#', html:'Compact edition'}]},
{tag:'li', id:this.getId('httpAuthTab'), children:[{tag:'a', href:'#', html:'HTTP Auth'}]}
]}
]}
]},
{tag:'div', cls:'body', children:[
{tag:'div', cls:'accountPanel', children:[
{tag:'div', cls:'subPanelContent', children:[
{tag:'ul', children:[
{tag:'li', id:this.getId('passwordGeneratorPanel'), children:[
- {tag:'h3', html:"Password generator"}
+// {tag:'h3', html:"Password generator"}
]},
{tag:'li', id:this.getId('bookmarkletPanel'), children:[
- {tag:'h3', html:"Bookmarklet"}
+// {tag:'h3', html:"Bookmarklet"}
]},
{tag:'li', id:this.getId('compactEditionPanel'), children:[
- {tag:'h3', html:"Compact edition"}
+// {tag:'h3', html:"Compact edition"}
]},
{tag:'li', id:this.getId('httpAuthPanel'), children:[
- {tag:'h3', html:"HTTP Auth"}
+// {tag:'h3', html:"HTTP Auth"}
]}
]}
]}
]}
]},
{tag:'div', cls:'footer'}
]);
this.tabPanelController().setup({selected:this.initiallySelectedTab()});
},
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js b/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js
index 9a0e744..1ab2e69 100644
--- a/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js
+++ b/frontend/gamma/js/Clipperz/PM/UI/Web/Controllers/AppController.js
@@ -214,48 +214,50 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
var page;
var user;
slot = args.slot;
user = args.user;
this.setUser(user);
slot.setContent(this.appPage());
this.appPage().slotNamed('userInfoBox').setContent(this.userInfoBox());
this.appPage().slotNamed('tabSidePanel').setContent(this.tabSidePanel());
this.appPage().slotNamed('accountPanel').setContent(this.accountPanel());
this.appPage().slotNamed('dataPanel').setContent(this.dataPanel());
this.appPage().slotNamed('toolsPanel').setContent(this.toolsPanel());
this.hideAllAppPageTabSlots();
this.appPage().showSlot(this.slotNameForTab('cards'));
MochiKit.Signal.connect(this.tabSidePanel(), 'tabSelected', this, 'handleTabSelected');
MochiKit.Signal.connect(this.tabSidePanel(), 'addCard', this, 'handleAddCard');
MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'addCard', this, 'handleAddCard');
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy', this, 'handleDownloadOfflineCopy');
+
deferredResult = new Clipperz.Async.Deferred("AppController.run", {trace:false});
deferredResult.addMethod(this.cardsController(), 'run', {slot:this.appPage().slotNamed('cardGrid'), user:user});
// deferredResult.addMethod(this.directLoginsController(), 'run', {slot:this.appPage().slotNamed('directLoginGrid'), user:user});
deferredResult.addMethod(this, 'populateUserInfo');
deferredResult.addCallback(MochiKit.Visual.ScrollTo, 'miscLinks', {duration:0});
deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'CARDS_CONTROLLER_DID_RUN');
deferredResult.addMethod(this.tabSidePanel(), 'selectTab', 'cards');
deferredResult.callback();
},
//-----------------------------------------------------------------------------
'handleTabSelected': function (selectedTabName) {
var aTabName;
var aSlotName;
//Clipperz.log(">>> AppController.handleTabSelected", selectedTabName);
this.hideAllAppPageTabSlots();
this.appPage().showSlot(this.slotNameForTab(selectedTabName));
switch (selectedTabName) {
case 'cards':
@@ -300,27 +302,50 @@ MochiKit.Base.update(Clipperz.PM.UI.Web.Controllers.AppController.prototype, {
return deferredResult;
},
//-----------------------------------------------------------------------------
'handleLock': function (anEvent) {
return Clipperz.Async.callbacks("AppController.handleLock", [
MochiKit.Base.method(this.cardsController(), 'deleteAllCleanTextData'),
MochiKit.Base.method(this.user(), 'lock')
], {trace:false});
},
//.............................................................................
'handleUnlock': function (anEvent) {
return Clipperz.Async.callbacks("AppController.handleUnock", [
MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress'),
MochiKit.Base.method(this.user(), 'login'),
MochiKit.Base.method(this.cardsController(), 'focus'),
MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'progressDone'),
MochiKit.Base.method(this.userInfoBox(), 'unlock')
], {trace:false});
},
+ 'handleDownloadOfflineCopy': function (anEvent) {
+console.log("AppController.handleDownloadOfflineCopy");
+ var downloadHref;
+
+ downloadHref = window.location.href.replace(/\/[^\/]*$/,'') + Clipperz_dumpUrl;
+
+ if (Clipperz_IEisBroken == true) {
+ window.open(downloadHref, "");
+ } else {
+ var deferredResult;
+ var newWindow;
+
+ newWindow = window.open("", "");
+
+ deferredResult = new Clipperz.Async.Deferred("AppController.handleDownloadOfflineCopy", {trace:true});
+ deferredResult.addCallback(MochiKit.Base.method(this.user().connection(), 'message'), 'echo', {'echo':"echo"});
+ deferredResult.addCallback(function(aWindow) {
+ aWindow.location.href = downloadHref;
+ }, newWindow);
+ deferredResult.callback();
+ }
+ },
+
//=============================================================================
__syntaxFix__: "syntax fix"
});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js
deleted file mode 100644
index 5380aa1..0000000
--- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/CardDetail.js
+++ b/dev/null
@@ -1,163 +0,0 @@
-/*
-
-Copyright 2008-2011 Clipperz Srl
-
-This file is part of Clipperz Community Edition.
-Clipperz Community Edition is an online password manager.
-For further information about its features and functionalities please
-refer to http://www.clipperz.com.
-
-* Clipperz Community Edition is free software: you can redistribute
- it and/or modify it under the terms of the GNU Affero General Public
- License as published by the Free Software Foundation, either version
- 3 of the License, or (at your option) any later version.
-
-* Clipperz Community Edition is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU Affero General Public License for more details.
-
-* You should have received a copy of the GNU Affero General Public
- License along with Clipperz Community Edition. If not, see
- <http://www.gnu.org/licenses/>.
-
-*/
-
-Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components');
-
-Clipperz.PM.UI.iPhone.Components.CardDetail = function(args) {
- args = args || {};
-
- Clipperz.PM.UI.iPhone.Components.CardDetail.superclass.constructor.apply(this, arguments);
-
- this._cardReference = null;
-
- return this;
-}
-
-//=============================================================================
-
-Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.CardDetail, Clipperz.PM.UI.Common.Components.BaseComponent, {
-
- //-------------------------------------------------------------------------
-
- 'toString': function () {
- return "Clipperz.PM.UI.iPhone.Components.CardDetail component";
- },
-
- //-------------------------------------------------------------------------
-
- 'cardReference': function () {
- return this._cardReference;
- },
-
- 'setCardReference': function (aValue) {
- this._cardReference = aValue;
- },
-
- //-------------------------------------------------------------------------
-
- 'renderSelf': function(/*aContainer, aPosition*/) {
- this.append(this.element(), [
- {tag:'div', cls:'cardDetail', id:this.getId('cardDetail'), children:[
- {tag:'div', id:this.getId('progressBar')} //,
-// {tag:'h1', cls:'loading', html:"loading"}
- ]}
- ]);
-
- this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
- MochiKit.Signal.signal(Clipperz.PM.UI.Common.Controllers.ProgressBarController.defaultController, 'updateProgress', 0);
- },
-
- //=========================================================================
-
- 'showCardDetails': function (someData) {
- this.element().innerHTML = '';
- this.append(this.element(), [
- {tag:'fieldset', id:this.getId('fields'), children:MochiKit.Base.map(function (aFieldData) {
- return {tag:'div', cls:'row', children:[
- {tag:'label', html:aFieldData['label']},
-// {tag:'span', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), html:aFieldData['value']}
- {tag:'div', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), children:[
- {tag:'div', children:[{tag:'p', html:aFieldData['value']}]}
- ]}
-// {tag:'input', type:'text', cls:('fieldValue ' + (aFieldData['isHidden'] ? 'password' : 'text')), value:aFieldData['value'], disabled:true}
-
- ]}
- }, someData['fields'])}
- ]);
-
- MochiKit.Iter.forEach(MochiKit.Selector.findChildElements(this.element(), ['span.password']), MochiKit.Base.bind(function (aPasswordElement) {
- MochiKit.Signal.connect(aPasswordElement, 'onclick', function (anEvent) { alert(MochiKit.DOM.scrapeText(anEvent.src())); })
- }, this));
-
- if (someData['directLogins'].length > 0) {
- this.append(this.element(), [
- {tag:'h2', html:"Direct logins"},
- {tag:'fieldset', id:this.getId('directLogins'), children:MochiKit.Base.map(function (aDirectLoginData) {
- return {tag:'div', cls:'row', id:('directLogin_' + aDirectLoginData['_reference']), children:[
- {tag:'img', cls:'favicon', src:aDirectLoginData['favicon']},
-// {tag:'input', cls:'directLogin', disabled:'disabled', type:'text', name:aDirectLoginData['label'], value:aDirectLoginData['label']}
- {tag:'span', cls:'directLogin', html:aDirectLoginData['label']}
- ]}
- }, someData['directLogins'])}
- ]);
-
- MochiKit.Base.map(MochiKit.Base.bind(function (aRowNode) {
- MochiKit.Signal.connect(aRowNode, 'onclick', this, 'directLoginClickHandler');
- }, this),
- MochiKit.Selector.findChildElements(this.getElement('directLogins'), ['div.row'])
- )
- };
-
- if (someData['notes'] != '') {
- this.append(this.element(), [
- {tag:'h2', html:"Notes"},
- {tag:'fieldset', id:this.getId('fieldset'), children:[
- {tag:'div', cls:'row notes', children:[
- {tag:'span', html:someData['notes']}
- ]}
- ]}
- ]);
- };
-
- return true;
- },
-
- //-------------------------------------------------------------------------
-/*
- 'toggleClickHandler': function (anEvent) {
- var nextState;
- var fieldValue;
-
-//console.log("TOGGLE");
- anEvent.preventDefault;
- fieldValue = MochiKit.Selector.findChildElements(anEvent.src().parentNode.parentNode, ['span.password'])[0];
-
- nextState = (MochiKit.DOM.getNodeAttribute(anEvent.src(), 'toggled') != 'true');
- if (nextState) {
- MochiKit.DOM.removeElementClass(fieldValue, 'clear');
- } else {
- MochiKit.DOM.addElementClass(fieldValue, 'clear');
- }
-
- MochiKit.DOM.setNodeAttribute(anEvent.src(), 'toggled', nextState);
- },
-*/
- //=========================================================================
-
- 'directLoginClickHandler': function (anEvent) {
- anEvent.preventDefault();
-
- if (/(directLogin_)/.test(anEvent.src().id)) {
- var directLoginReference;
-
- directLoginReference = anEvent.src().id.match(/(directLogin_)(.*)/)[2];
- MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', {cardReference:this.cardReference(), directLoginReference:directLoginReference});
- }
- },
-
- //=========================================================================
-
- __syntaxFix__: "syntax fix"
-});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js
deleted file mode 100644
index 5341878..0000000
--- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Components/LoginForm.js
+++ b/dev/null
@@ -1,178 +0,0 @@
-/*
-
-Copyright 2008-2011 Clipperz Srl
-
-This file is part of Clipperz Community Edition.
-Clipperz Community Edition is an online password manager.
-For further information about its features and functionalities please
-refer to http://www.clipperz.com.
-
-* Clipperz Community Edition is free software: you can redistribute
- it and/or modify it under the terms of the GNU Affero General Public
- License as published by the Free Software Foundation, either version
- 3 of the License, or (at your option) any later version.
-
-* Clipperz Community Edition is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU Affero General Public License for more details.
-
-* You should have received a copy of the GNU Affero General Public
- License along with Clipperz Community Edition. If not, see
- <http://www.gnu.org/licenses/>.
-
-*/
-
-Clipperz.Base.module('Clipperz.PM.UI.iPhone.Components');
-
-Clipperz.PM.UI.iPhone.Components.LoginForm = function(args) {
- args = args || {};
-
- Clipperz.PM.UI.iPhone.Components.LoginForm.superclass.constructor.apply(this, arguments);
-
- return this;
-}
-
-//=============================================================================
-
-Clipperz.Base.extend(Clipperz.PM.UI.iPhone.Components.LoginForm, Clipperz.PM.UI.Common.Components.BaseComponent, {
-
- //-------------------------------------------------------------------------
-
- 'toString': function () {
- return "Clipperz.PM.UI.iPhone.Components.LoginForm component";
- },
-
- //-------------------------------------------------------------------------
-
- 'focusOnUsername': function () {
- this.getElement('username').focus();
- },
-
- //-------------------------------------------------------------------------
-
- 'username': function () {
- return this.getElement('username').value;
- },
-
- 'passphrase': function () {
- return this.getElement('passphrase').value;
- },
-
- //-------------------------------------------------------------------------
-
- 'renderSelf': function(/*aContainer, aPosition*/) {
- this.append(this.element(), [
- {tag:'div', cls:'toolbar iPhoneClipperzToolbar', children:[
- {tag:'h1', id:'pageTitle', html:'Clipperz'},
- {tag:'a', id:'backButton', cls:'button', href:'#', html:"back"}
- ]},
- {tag:'form', title:'Theaters', cls:'panel toolbarlessPanel loginForm', id:this.getId('loginFormPanel'), children:[
- {tag:'fieldset', id:this.getId('fieldset'), children:[
- {tag:'div', cls:'row', children:[
- {tag:'label', html:"username"},
- {tag:'input', type:'text', name:'username', value:"", autocorrect:'off', autocapitalize:'off', id:this.getId('username')}
- ]},
- {tag:'div', cls:'row', children:[
- {tag:'label', html:"passphrase"},
- {tag:'input', type:'password', name:'passphrase', value:"", id:this.getId('passphrase')}
- ]}
- ]},
- {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Login", id:this.getId('submit')}
- ]},
- {tag:'div', cls:'panel toolbarlessPanel loginProgressPanel', id:this.getId('loginProgressPanel'), children:[
- {tag:'div', id:this.getId('progressBar')} //,
-// {tag:'a', cls:'whiteButton', type:'submit', href:'#', html:"Cancel", id:this.getId('cancel')}
- ]},
- {tag:'div', cls:'panel loginErrorPanel', id:this.getId('loginErrorPanel'), children:[
- {tag:'div', cls:'errorMessage', id:this.getId('errorMessageBox'), children:[
- {tag:'h2', id:this.getId('errorMessage'), html:"Login failed"}
- ]}
- ]}
- ]);
-
- MochiKit.Signal.connect(this.getElement('submit'), 'onclick', this, 'submitHandler');
- MochiKit.Signal.connect(this.getElement('loginFormPanel'), 'onsubmit', this, 'submitHandler');
-
-// MochiKit.Signal.connect(this.getElement('cancel'), 'onclick', this, 'cancelHandler');
- MochiKit.Signal.connect('backButton', 'onclick', this, 'backHandler');
-
- this.addComponent(new Clipperz.PM.UI.Common.Components.ProgressBar({'element':this.getElement('progressBar')}));
-
-// MochiKit.Style.hideElement(this.getElement('errorMessage'));
-
- this.showLoginForm();
-// MochiKit.Async.callLater(0.2, MochiKit.Base.method(this, 'focusOnUsername'));
- },
-
- //-------------------------------------------------------------------------
-
- 'showLoginForm': function () {
- MochiKit.Style.showElement(this.getElement('loginFormPanel'));
- MochiKit.Style.hideElement(this.getElement('loginProgressPanel'));
- MochiKit.Style.hideElement(this.getElement('loginErrorPanel'));
- MochiKit.Style.hideElement('backButton');
- },
-
- 'slideInLoginForm': function () {
- var offset;
-
- offset = ((MochiKit.DOM.getNodeAttribute(MochiKit.DOM.currentDocument().body, 'orientation') == 'portrait') ? 320 : 480);
-
- MochiKit.Style.showElement(this.getElement('loginFormPanel'));
- MochiKit.Style.setElementPosition(this.getElement('loginFormPanel'), {x:-offset, y:0});
-
- new MochiKit.Visual.Sequence([
- new MochiKit.Visual.Parallel([
- new MochiKit.Visual.Move(this.getElement('loginErrorPanel'), {x:offset, y:0, mode:'relative', transition:MochiKit.Visual.Transitions.linear, sync:true}),
- new MochiKit.Visual.Move(this.getElement('loginFormPanel'), {x:0, y:0, mode:'absolute', transition:MochiKit.Visual.Transitions.linear, sync:true}),
- MochiKit.Visual.fade ('backButton', { transition:MochiKit.Visual.Transitions.linear, sync:true})
- ], {duration:0.5, sync:true}),
- MochiKit.Visual.fade(this.getElement('loginErrorPanel'), {duration:0, sync:true})
- ], {})
- },
-
- 'showLoginProgress': function () {
- MochiKit.Style.hideElement(this.getElement('loginFormPanel'));
- MochiKit.Style.showElement(this.getElement('loginProgressPanel'));
- },
-
- 'showLoginError': function (anError) {
- this.getElement('errorMessage').innerHTML = "Login error";
-
- MochiKit.Style.showElement('backButton');
- MochiKit.Style.hideElement(this.getElement('loginProgressPanel'));
- MochiKit.Style.showElement(this.getElement('loginErrorPanel'));
- MochiKit.Style.setElementPosition(this.getElement('loginErrorPanel'), {x:0, y:45});
- },
-
- //-------------------------------------------------------------------------
-/*
- 'disableCancelButton': function () {
- MochiKit.DOM.hideElement(this.getElement('cancel'));
- },
-*/
- //-------------------------------------------------------------------------
-
- 'submitHandler': function (anEvent) {
- anEvent.preventDefault();
-
- MochiKit.Signal.signal(this, 'doLogin', {'username':this.username(), 'passphrase':this.passphrase()});
- },
-
- 'cancelHandler': function (anEvent) {
- anEvent.preventDefault();
-
-//console.log("CANCEL");
- },
-
- 'backHandler': function (anEvent) {
- anEvent.preventDefault();
-
- this.slideInLoginForm();
- },
-
- //-------------------------------------------------------------------------
-
- __syntaxFix__: "syntax fix"
-});
diff --git a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js b/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js
deleted file mode 100644
index 3fcaae1..0000000
--- a/frontend/gamma/js/Clipperz/PM/UI/iPhone/Controllers/MainController.js
+++ b/dev/null
@@ -1,369 +0,0 @@
-/*
-
-Copyright 2008-2011 Clipperz Srl
-
-This file is part of Clipperz Community Edition.
-Clipperz Community Edition is an online password manager.
-For further information about its features and functionalities please
-refer to http://www.clipperz.com.
-
-* Clipperz Community Edition is free software: you can redistribute
- it and/or modify it under the terms of the GNU Affero General Public
- License as published by the Free Software Foundation, either version
- 3 of the License, or (at your option) any later version.
-
-* Clipperz Community Edition is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU Affero General Public License for more details.
-
-* You should have received a copy of the GNU Affero General Public
- License along with Clipperz Community Edition. If not, see
- <http://www.gnu.org/licenses/>.
-
-*/
-
-Clipperz.Base.module('Clipperz.PM.UI.iPhone.Controllers');
-
-// Some parts of this controller have been derived from the iUI library.
-
-Clipperz.PM.UI.iPhone.Controllers.MainController = function() {
- this._loginForm = null;
- this._cardList = null;
- this._cachedValues = null;
- this._user = null;
-
- if (typeof window.onorientationchange == 'object') {
- MochiKit.Signal.connect(window, 'onorientationchange', this, 'orientationChangeHandler');
- MochiKit.Async.callLater(0, MochiKit.Base.method(this, 'orientationChangeHandler'));
- } else {
- this.setOrientation('portrait');
-// this.setOrientation('landscape');
- }
-
- this.addMetaTag('viewport', 'width=devicewidth; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;');
- this.addMetaTag('apple-mobile-web-app-capable', 'yes');
- this.addMetaTag('apple-mobile-web-app-status-bar-style', 'black');
-
- this.addLinkTag('apple-touch-icon', 'data:image/png;charset=utf-8;base64,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');
-// this.addLinkTag('apple-touch-startup-image', 'default.png');
-
-// if (!window.navigator.standalone) // not running as an installed app
-
- MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'selectedDirectLogin', this, 'selectedDirectLoginHandler');
-
- MochiKit.DOM.addElementClass(document.body, 'iPhone');
- return this;
-}
-
-MochiKit.Base.update(Clipperz.PM.UI.iPhone.Controllers.MainController.prototype, {
-
- 'toString': function () {
- return "Clipperz.PM.UI.iPhone.Controllers.MainController";
- },
-
- //=========================================================================
-
- 'user': function () {
- return this._user;
- },
-
- 'setUser': function (aValue) {
- this._user = aValue;
- },
-
- //=========================================================================
-
- 'loginForm': function() {
- if (this._loginForm == null) {
- MochiKit.DOM.removeElement('mainDiv');
- this._loginForm = new Clipperz.PM.UI.iPhone.Components.LoginForm({element:MochiKit.DOM.currentDocument().body});
- MochiKit.Signal.connect(this._loginForm, 'doLogin', this, 'doLoginHandler')
- }
-
- return this._loginForm;
- },
-
- 'removeLoginForm': function () {
- if (this._loginForm != null) {
- this._loginForm.remove();
- this._loginForm = null;
- }
- },
-
- //-----------------------------------------------------------------------------
-
- 'cardList': function () {
- if (this._cardList == null) {
- this._cardList = new Clipperz.PM.UI.iPhone.Components.CardList({element:MochiKit.DOM.currentDocument().body});
- MochiKit.Signal.connect(this._cardList, 'searchEvent', this, 'searchEventHandler')
- MochiKit.Signal.connect(this._cardList, 'selectedCard', this, 'selecetedCardHandler')
- }
-
- return this._cardList;
- },
-
- //=========================================================================
-
- 'currentWidth': function () {
- return this._currentWidth;
- },
-
- 'setCurrentWidth': function (aValue) {
- this._currentWidth = aValue;
- },
-
- //=========================================================================
-
- 'orientationChangeHandler': function () {
- switch(window.orientation) {
- case 0:
- this.setOrientation('portrait');
- break;
- case 90:
- case -90:
- this.setOrientation('landscape');
- break;
- }
- },
-
- //-------------------------------------------------------------------------
-
- 'setOrientation': function (anOrientation) {
- document.body.setAttribute('orientation', anOrientation);
- setTimeout(scrollTo, 100, 0, 1);
- },
-
- //-------------------------------------------------------------------------
-
- 'slidePages': function (fromPage, toPage, backwards) {
- var axis;
- var slideDone;
-
- slideDone = function () {
- // console.log("slideDone");
- if (!hasClass(toPage, "dialog")) {
- fromPage.removeAttribute("selected");
- }
- checkTimer = setInterval(checkOrientAndLocation, 300);
- setTimeout(updatePage, 0, toPage, fromPage);
- fromPage.removeEventListener('webkitTransitionEnd', slideDone, false);
- }
-
- axis = (backwards ? fromPage : toPage).getAttribute("axis");
-
- clearInterval(checkTimer);
-
- if (canDoSlideAnim() && axis != 'y') {
- slide2(fromPage, toPage, backwards, slideDone);
- } else {
- slide1(fromPage, toPage, backwards, axis, slideDone);
- }
- },
-
- //-------------------------------------------------------------------------
-
- 'getCachedValues': function () {
- var deferredResult;
-
- if (this._cachedObjects != null) {
- deferredResult = MochiKit.Async.succeed(this._cachedObjects);
- } else {
- deferredResult = new Clipperz.Async.Deferred("MainController.getCachedValues", {trace:false});
- deferredResult.addMethod(this.user(), 'getRecords');
- deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.getCachedValues - collectResults", {
- '_rowObject': MochiKit.Async.succeed,
- '_reference': MochiKit.Base.methodcaller('reference'),
- 'label': MochiKit.Base.methodcaller('label'),
- 'favicon': MochiKit.Base.methodcaller('favicon'),
- '_searchableContent': MochiKit.Base.methodcaller('searchableContent')
- }, {trace:false}));
- deferredResult.addCallback(Clipperz.Async.collectAll);
- deferredResult.addCallback(MochiKit.Base.bind(function (someRows) {
- this._cachedObjects = someRows;
- return this._cachedObjects;
- }, this));
- deferredResult.callback();
- }
-
- return deferredResult;
- },
- //=========================================================================
-
- 'run': function(shouldShowRegistrationForm) {
- this.loginForm().render();
- MochiKit.Async.callLater(1, MochiKit.Base.method(this.loginForm(), 'focusOnUsername'));
- },
-
- //=========================================================================
-
- 'doLoginHandler': function (someArgs) {
- var deferredResult;
- var parameters;
- var shouldUseOTP;
-// var loginProgress;
- var user;
- var getPassphraseDelegate;
-
- parameters = someArgs;
- shouldUseOTP = (typeof(parameters.passphrase) == 'undefined');
-
- getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, parameters.passphrase);
- user = new Clipperz.PM.DataModel.User({'username':parameters.username, 'getPassphraseFunction':getPassphraseDelegate});
-
- deferredResult = new Clipperz.Async.Deferred("MainController.doLogin", {trace:false});
- deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'initProgress', {'steps':4});
- deferredResult.addMethod(this.loginForm(), 'showLoginProgress');
- deferredResult.addCallback(MochiKit.Async.wait, 0.1);
- deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
- deferredResult.addMethod(user, 'login');
- deferredResult.addMethod(this, 'setUser', user);
- deferredResult.addMethod(user, 'getRecords');
- deferredResult.addMethod(this, 'removeLoginForm');
- deferredResult.addMethod(this.cardList(), 'render');
- deferredResult.addMethod(this, 'displaySelectedRecords', '');
- deferredResult.addErrback(MochiKit.Base.method(this.loginForm(), 'showLoginError'));
- deferredResult.callback();
-
- return deferredResult;
- },
-
- //=========================================================================
-
- 'searchEventHandler': function (aValue) {
-//console.log("searching for ... " + aValue);
- return this.displaySelectedRecords(aValue);
- },
-
- //=========================================================================
-
- '_displaySelectedRows': function (aFilter, someRows) {
- var result;
-
- result = someRows;
-
- if (aFilter != null) {
- var filter;
- var filterRegExp;
-
- filter = aFilter.replace(/[^A-Za-z0-9]/g, "\\$&");
- filterRegExp = new RegExp(filter, "i");
- result = MochiKit.Base.filter(function (aCachedResult) { return filterRegExp.test(aCachedResult['_searchableContent'])}, result);
- }
-
-
- result.sort(MochiKit.Base.partial(function (aKey, aComparator, aObject, bObject) {
- return aComparator(aObject[aKey], bObject[aKey]);
- }, 'label', Clipperz.Base.caseInsensitiveCompare));
-
- this.cardList().update(result);
- },
-
- //-------------------------------------------------------------------------
-
- 'displaySelectedRecords': function (aFilter) {
- return Clipperz.Async.callbacks("MainController.displaySelectedrows", [
- MochiKit.Base.method(this, 'getCachedValues'),
- MochiKit.Base.method(this, '_displaySelectedRows', aFilter)
- ], {trace:false});
- },
-
- //=========================================================================
-
- 'selecetedCardHandler': function (aRecordReference) {
- var deferredResult;
- var recordData;
-
- recordData = {};
-//console.log("Showing detail for card with reference", aRecordReference);
- deferredResult = new Clipperz.Async.Deferred("MainController.selectedCardHandler", {trace:false});
- deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
- deferredResult.collectResults({
- '_reference': MochiKit.Base.methodcaller('reference'),
- 'title': MochiKit.Base.methodcaller('label'),
- 'favicon': MochiKit.Base.methodcaller('favicon')
- });
- deferredResult.addCallback(function (someData) {
- MochiKit.Base.update(recordData, someData);
- })
- deferredResult.addMethod(this.cardList(), 'showCard', recordData);
-
- deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
- deferredResult.addMethodcaller('notes');
- deferredResult.addCallback(function (someNotes) {
- recordData['notes'] = someNotes;
- })
-
- deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
- deferredResult.addMethodcaller('getCurrentRecordVersion');
- deferredResult.addMethodcaller('fields');
- deferredResult.addCallback(MochiKit.Base.values);
- deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - fields", {
- 'label': MochiKit.Base.methodcaller('label'),
- 'value': MochiKit.Base.methodcaller('value'),
- 'isHidden': MochiKit.Base.methodcaller('isHidden')
- }, {trace:false}));
- deferredResult.addCallback(Clipperz.Async.collectAll);
- deferredResult.addCallback(function (someData) {
- recordData['fields'] = someData;
- });
-
- deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
- deferredResult.addMethodcaller('directLogins');
- deferredResult.addCallback(MochiKit.Base.values);
- deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("MainController.selectedCardHandler - directLogins", {
- 'label': MochiKit.Base.methodcaller('label'),
- 'favicon': MochiKit.Base.methodcaller('favicon'),
- '_reference': MochiKit.Base.methodcaller('reference')
- }, {trace:false}));
- deferredResult.addCallback(Clipperz.Async.collectAll);
- deferredResult.addCallback(function (someData) {
- recordData['directLogins'] = someData;
- });
-
- deferredResult.addMethod(this.cardList(), 'showCardDetails', recordData);
- deferredResult.callback();
-
- return deferredResult;
- },
-
- //=========================================================================
-
- 'selectedDirectLoginHandler': function (someData) {
- var deferredResult;
-
-//console.log("<<< signal - directLogin");
- deferredResult = new Clipperz.Async.Deferred("MainController.selectedDirectLoginHandler", {trace:false});
- deferredResult.addMethod(this.user(), 'getRecord', someData['cardReference']);
- deferredResult.addMethodcaller('directLogins');
- deferredResult.addCallback(MochiKit.Base.itemgetter(someData['directLoginReference']));
-// deferredResult.addMethodcaller('runDirectLogin');
- deferredResult.addCallback(Clipperz.PM.UI.Common.Controllers.DirectLoginRunner.openDirectLogin);
- deferredResult.callback();
-
- return deferredResult;
- },
-
- //=========================================================================
-
- 'addMetaTag': function (aName, aContent) {
- var metaTag;
-
- metaTag = document.createElement('meta');
- metaTag.name = aName;
- metaTag.content = aContent;
- document.getElementsByTagName('head')[0].appendChild(metaTag);
- },
-
- 'addLinkTag': function (aRel, anHref) {
- var linkTag;
-
- linkTag = document.createElement('link');
- linkTag.rel = aRel;
- linkTag.href = anHref;
- document.getElementsByTagName('head')[0].appendChild(linkTag);
- },
-
- //=========================================================================
- __syntaxFix__: "syntax fix"
-}); \ No newline at end of file
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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ (c) 2012 by jQTouch project members.
+ See LICENSE.txt for license.
+
+ Author: Thomas Yip
+*/
+
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ (c) 2012 by jQTouch project members.
+ See LICENSE.txt for license.
+
+ Author: Thomas Yip
+*/
+
+(function($) {
+ var src = $("head script").last().attr("src") || '';
+ var scriptpath = src.split('?')[0].split('/').slice(0, -1).join('/')+'/';
+ var csspath = scriptpath + 'jqt.actionsheet.css';
+ var link = $('<link href="' + csspath + '" rel="stylesheet">');
+ $('head').append($(link));
+
+ function hide(callback) {
+ var $target = $(this);
+ var data = $(this).data('actionsheet');
+ var $source = data.source;
+
+ var timeout;
+
+ function cleanup() {
+ clearTimeout(timeout);
+
+ $source.removeClass('transition');
+ $target.removeClass('inmotion transition');
+ !callback || callback.apply(this, arguments);
+ };
+ timeout = setTimeout(cleanup, 500);
+
+ if (data.shown) {
+ $(this).data('actionsheet', {});
+ $target.one('webkitTransitionEnd', cleanup);
+
+ $source.addClass('transition');
+ $target.removeClass('current').addClass('inmotion transition');
+ $('#jqt').removeClass('actionopened');
+ }
+ return $target;
+ }
+
+ function show(callback) {
+ var $target = $(this);
+ var data = $(this).data('actionsheet') || {};
+ if (!data.shown) {
+ var $source = $('#jqt .current:not(.actionsheet)');
+
+ $target.one('webkitTransitionEnd', function() {
+ $source.removeClass('transition');
+ $target.removeClass('inmotion transition');
+ !callback || callback.apply(this, arguments);
+ });
+
+ data.shown = true;
+ data.source = $source;
+ $(this).data('actionsheet', data);
+
+ $source.addClass('transition');
+ $target.addClass('inmotion transition');
+ $('#jqt').addClass('actionopened');
+ setTimeout(function() {
+ $target.addClass('current');
+ }, 50);
+ }
+ return $target;
+ }
+
+ var methods = {
+ init: function(options) {
+ $(this).addClass('actionsheet');
+ $(this).data({shown: false});
+ },
+ show: show,
+ hide: hide
+ };
+
+ $.fn.actionsheet = function(method) {
+ if (methods[method]) {
+ if ($(this).is('.actionsheet')) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else {
+ var msg = 'Target is not a `actionsheet`. Action `' + method + '` is ignored.';
+ console.warn(msg);
+ }
+ } else if ( typeof method === 'object' || ! method ) {
+ return methods.init.apply(this, arguments);
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
+ }
+ };
+
+ if ($.jQTouch) {
+ $.jQTouch.addTapHandler({
+ name: 'open-actionsheet',
+ isSupported: function(e, params) {
+ return params.$el.is('.action');
+ },
+ fn: function(e, params) {
+ params.$el.removeClass('active');
+
+ var $target = $(params.hash);
+ $target.actionsheet('show');
+
+ return false;
+ }
+ });
+ $.jQTouch.addTapHandler({
+ name: 'follow-actionlink',
+ isSupported: function(e, params) {
+ if ($('#jqt').hasClass('actionopened')) {
+ return params.$el.is('.actionsheet a');
+ }
+ return false;
+ },
+ fn: function(e, params) {
+ params.$el.removeClass('active');
+
+ var $target = params.$el.closest('.actionsheet');
+ $target.actionsheet('hide', function() {
+ if (!params.$el.is('.dismiss')) {
+ params.$el.trigger('tap');
+ }
+ });
+ return false;
+ }
+ });
+ } else {
+ console.error('Extension `jqt.actionsheet` failed to load. jQT not found');
+ }
+})($);
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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Created by David Kaneda <http://www.davidkaneda.com>
+ Maintained by Thomas Yip <http://beedesk.com/>
+ Sponsored by Sencha Labs <http://www.sencha.com/>
+ Special thanks to Jonathan Stark <http://www.jonathanstark.com/>
+
+ Documentation and issue tracking on GitHub <http://github.com/senchalabs/jQTouch/>
+
+ (c) 2009-2011 Sencha Labs
+ jQTouch may be freely distributed under the MIT license.
+
+*/
+
+(function($) {
+ if ($.jQTouch)
+ {
+ $.jQTouch.addExtension(function AutoTitles(jQT){
+
+ var titleSelector='.toolbar h1';
+
+ $(function(){
+ $('#jqt').bind('pageAnimationStart', function(e, data){
+ if (data.direction === 'in'){
+ var $title = $(titleSelector, $(e.target));
+ var $ref = $(e.target).data('referrer');
+ if ($title.length && $ref && $title.text() === ''){
+ $title.html($ref.text());
+ }
+ }
+ });
+ });
+
+ function setTitleSelector(ts){
+ titleSelector=ts;
+ }
+
+ return {
+ setTitleSelector: setTitleSelector
+ };
+
+ });
+ }
+})($);
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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Created by David Kaneda <http://www.davidkaneda.com>
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ Special thanks to Jonathan Stark <http://jonathanstark.com/>
+ and pinch/zoom <http://www.pinchzoom.com/>
+
+ (c) 2009 by jQTouch project members.
+ See LICENSE.txt for license.
+
+*/
+
+(function($) {
+ if ($.jQTouch)
+ {
+ $.jQTouch.addExtension(function Floaty(jQT){
+
+ $.fn.makeFloaty = function(options){
+ var defaults = {
+ align: 'top',
+ spacing: 20,
+ time: '.3s'
+ };
+
+ var settings = $.extend({}, defaults, options);
+
+ settings.align = (settings.align == 'top') ? 'top' : 'bottom';
+
+ return this.each(function(){
+ var $el = $(this);
+
+ $el.css({
+ '-webkit-transition': 'top ' + settings.time + ' ease-in-out',
+ 'display': 'block',
+ 'min-height': '0 !important'
+ }).data('settings', settings);
+
+ $(document).scroll(function(){
+ if ($el.data('floatyVisible') === 'true')
+ {
+ $el.scrollFloaty();
+ }
+ });
+ $el.scrollFloaty();
+ });
+ };
+
+ $.fn.scrollFloaty = function(){
+
+
+ return this.each(function(){
+ var $el = $(this);
+ var settings = $el.data('settings'); // Settings not being set as object w/Zepto
+ var wHeight = $('html').attr('clientHeight'); // WRONG
+
+ var newY = window.pageYOffset +
+ ((settings.align == 'top') ?
+ settings.spacing : wHeight - settings.spacing - $el.get(0).offsetHeight);
+
+ $el.css('top', newY).data('floatyVisible', true);
+ });
+ };
+
+ $.fn.hideFloaty = function(){
+ return this.each(function(){
+ var $el = $(this);
+ var oh = $el.get(0).offsetHeight;
+
+ $el.css('top', -oh-10).data('floatyVisible', false);
+ });
+ };
+
+ $.fn.toggleFloaty = function(){
+ return this.each(function(){
+ var $el = $(this);
+ if ($el.data('floatyVisible') === 'true'){
+ $el.hideFloaty();
+ }
+ else
+ {
+ $el.scrollFloaty();
+ }
+ });
+ };
+ });
+ }
+})($); \ 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 @@
+(function($) {
+ if ($.jQTouch)
+ {
+ $.jQTouch.addExtension(function Location(){
+
+ var latitude, longitude, callback, callback2;
+
+ function updateLocation(fn, fn2) {
+ if (navigator.geolocation)
+ {
+ callback = fn;
+ callback2 = fn2;
+ navigator.geolocation.getCurrentPosition(savePosition, failResponse);
+ return true;
+ } else {
+ console.log('Device not capable of geo-location.');
+ fn(false);
+ return false;
+ }
+ }
+ function failResponse(error){
+ if (callback2) {
+ callback2(error);
+ }
+ }
+ function savePosition(position) {
+ latitude = position.coords.latitude;
+ longitude = position.coords.longitude;
+ if (callback) {
+ callback(getLocation());
+ }
+ }
+ function getLocation() {
+ if (latitude && longitude) {
+ return {
+ latitude: latitude,
+ longitude: longitude
+ };
+
+ } else {
+ console.log('No location available. Try calling updateLocation() first.');
+ return false;
+ }
+ }
+ return {
+ updateLocation: updateLocation,
+ getLocation: getLocation
+ };
+ });
+ }
+})($); \ 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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ (c) 2012 by jQTouch project members.
+ See LICENSE.txt for license.
+
+ Author: Thomas Yip
+*/
+
+(function($) {
+ var src = $("head script").last().attr("src") || '';
+ var scriptpath = src.split('?')[0].split('/').slice(0, -1).join('/')+'/';
+ var csspath = scriptpath + 'jqt.menusheet.css';
+ var link = $('<link href="' + csspath + '" rel="stylesheet">');
+ $('head').append($(link));
+
+ function hide(callback) {
+ var $target = $(this);
+ var data = $(this).data('menusheet');
+ if (data.shown) {
+ $(this).data('menusheet', {});
+ var $source = data.source;
+ $source.unbind('touchstart mousedown', data.closehandler);
+ $source.one('webkitTransitionEnd', function() {
+ $source.removeClass('inmotion transition in');
+ $target.removeClass('inmotion out');
+ !callback || callback.apply(this, arguments);
+ });
+
+ $source.addClass('inmotion transition in');
+ $target.addClass('inmotion out').removeClass('current');
+ $('#jqt').removeClass('menuopened');
+ }
+ return $target;
+ }
+
+ function show(callback) {
+ var $target = $(this);
+ var data = $(this).data('menusheet') || {};
+ if (!data.shown) {
+ var $source = $('#jqt .current:not(.menusheet)');
+ var closehandler = function() {
+ $target.menusheet('hide');
+ return false;
+ };
+
+ $source.one('webkitTransitionEnd', function() {
+ $source.one('touchstart mousedown', closehandler);
+ $source.removeClass('inmotion transition out');
+ $target.removeClass('inmotion in');
+ !callback || callback.apply(this, arguments);
+ });
+
+ data.shown = true;
+ data.closehandler = closehandler;
+ data.source = $source;
+ $(this).data('menusheet', data);
+
+ $source.addClass('inmotion transition out');
+ $target.addClass('current in');
+ $('#jqt').addClass('menuopened');
+ }
+ return $target;
+ }
+
+ var methods = {
+ init: function(options) {
+ $(this).addClass('menusheet');
+ $(this).data({shown: false});
+ },
+ show: show,
+ hide: hide
+ };
+
+ $.fn.menusheet = function(method) {
+ if (methods[method]) {
+ if ($(this).is('.menusheet')) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else {
+ var msg = 'Target is not a `menusheet`. Action `' + method + '` is ignored.';
+ console.warn(msg);
+ }
+ } else if ( typeof method === 'object' || ! method ) {
+ return methods.init.apply(this, arguments);
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
+ }
+ };
+
+ if ($.jQTouch) {
+ $.jQTouch.addTapHandler({
+ name: 'open-menusheet',
+ isSupported: function(e, params) {
+ return params.$el.is('.menu');
+ },
+ fn: function(e, params) {
+ params.$el.removeClass('active');
+
+ var $target = $(params.hash);
+ $target.menusheet('show');
+
+ return false;
+ }
+ });
+ $.jQTouch.addTapHandler({
+ name: 'follow-menulink',
+ isSupported: function(e, params) {
+ if ($('#jqt').hasClass('menuopened')) {
+ return params.$el.is('.menusheet a');
+ }
+ return false;
+ },
+ fn: function(e, params) {
+ params.$el.removeClass('active');
+
+ var $target = params.$el.closest('.menusheet');
+ $target.menusheet('hide', function() {
+ if (!params.$el.is('.dismiss')) {
+ params.$el.trigger('tap');
+ }
+ });
+ return false;
+ }
+ });
+ } else {
+ console.error('Extension `jqt.menusheet` failed to load. jQT not found');
+ }
+})($);
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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Created by David Kaneda <http://www.davidkaneda.com>
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ Special thanks to Jonathan Stark <http://jonathanstark.com/>
+
+ Lots of this code is specifically derived from Jonathan's book,
+ "Building iPhone Apps with HTML, CSS, and JavaScript"
+
+ (c) 2009 by jQTouch project members.
+ See LICENSE.txt for license.
+
+*/
+
+(function($) {
+ if ($.jQTouch)
+ {
+ $.jQTouch.addExtension(function Offline(){
+
+ // Convenience array of status values
+ var cacheStatusValues = [];
+ cacheStatusValues[0] = 'uncached';
+ cacheStatusValues[1] = 'idle';
+ cacheStatusValues[2] = 'checking';
+ cacheStatusValues[3] = 'downloading';
+ cacheStatusValues[4] = 'updateready';
+ cacheStatusValues[5] = 'obsolete';
+
+ // Listeners for all possible events
+ var cache = window.applicationCache;
+ cache.addEventListener('cached', logEvent, false);
+ cache.addEventListener('checking', logEvent, false);
+ cache.addEventListener('downloading', logEvent, false);
+ cache.addEventListener('error', logEvent, false);
+ cache.addEventListener('noupdate', logEvent, false);
+ cache.addEventListener('obsolete', logEvent, false);
+ cache.addEventListener('progress', logEvent, false);
+ cache.addEventListener('updateready', logEvent, false);
+
+ // Log every event to the console
+ function logEvent(e) {
+ var online, status, type, message;
+ online = (isOnline()) ? 'yes' : 'no';
+ status = cacheStatusValues[cache.status];
+ type = e.type;
+ message = 'online: ' + online;
+ message+= ', event: ' + type;
+ message+= ', status: ' + status;
+ if (type == 'error' && navigator.onLine) {
+ message+= ' There was an unknown error, check your Cache Manifest.';
+ }
+ console.log(message);
+ }
+
+ function isOnline() {
+ return navigator.onLine;
+ }
+
+ if (!$('html').attr('manifest')) {
+ console.log('No Cache Manifest listed on the <html> tag.')
+ }
+
+ // Swap in newly download files when update is ready
+ cache.addEventListener('updateready', function(e){
+ // Don't perform "swap" if this is the first cache
+ if (cacheStatusValues[cache.status] != 'idle') {
+ cache.swapCache();
+ console.log('Swapped/updated the Cache Manifest.');
+ }
+ }
+ , false);
+
+ // These two functions check for updates to the manifest file
+ function checkForUpdates(){
+ cache.update();
+ }
+ function autoCheckForUpdates(){
+ setInterval(function(){cache.update()}, 10000);
+ }
+
+ return {
+ isOnline: isOnline,
+ checkForUpdates: checkForUpdates,
+ autoCheckForUpdates: autoCheckForUpdates
+ }
+ });
+ }
+})(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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Documentation and issue tracking on Google Code <http://code.google.com/p/jqtouch/>
+
+ (c) 2011 by jQTouch project members.
+ See LICENSE.txt for license.
+
+*/
+
+(function($) {
+ if ($.jQTouch) {
+
+ var scriptpath = $("script").last().attr("src").split('?')[0].split('/').slice(0, -1).join('/')+'/';
+
+ $.jQTouch.addExtension(function ThemeSwitcher(jQT) {
+
+ var current,
+ link,
+ titles = {},
+ defaults = {
+ themeStyleSelector: 'link[rel="stylesheet"][title]',
+ themeIncluded: [
+ {title: 'jQTouch', href: scriptpath + '../themes/css/jqtouch.css'},
+ {title: 'Apple', href: scriptpath + '../themes/css/apple.css'},
+ {title: 'Vanilla', href: scriptpath + '../themes/css/vanilla.css'}
+
+ ]
+ },
+ options = $.extend({}, defaults, jQT.settings);
+
+ function setStyleState(item, title) {
+ var $item = $(item);
+
+ if ($item.attr('title') === title) {
+ item.disabled = false; // workaround for Firefox on Zepto
+ $item.removeAttr('disabled');
+ } else {
+ item.disabled = true; // workaround for Firefox on Zepto
+ $item.attr('disabled', true);
+ }
+ }
+
+ function initializeStyleState(item, title) {
+ // and, workaround for WebKit by initializing the 'disabled' attribute
+ if (!current) {
+ current = title;
+ }
+ setStyleState(item, current);
+ }
+
+ // public
+ function switchStyle(title) {
+ current = title;
+ $(options.themeStyleSelector).each(function(i, item) {
+ setStyleState(item, title);
+ });
+ }
+
+ // collect title names, from <head>
+ $(options.themeStyleSelector).each(function(i, item) {
+ var $item = $(item);
+ var title = $item.attr('title');
+
+ titles[title] = true;
+
+ initializeStyleState(item, title);
+ });
+
+ // add included theme
+ for (var i=0; i < options.themeIncluded.length; i++) {
+ var hash = options.themeIncluded[i];
+ if (!(hash.title in titles)) {
+ link = $('<link title="' + hash.title + '" href="' + hash.href + '" rel="stylesheet">');
+ $('head').append($(link));
+
+ titles[hash.title] = true;
+
+ initializeStyleState(link, hash.title);
+ }
+ }
+
+ if (options.themeSelectionSelector) {
+ // create UI items
+ for (var title in titles) {
+ var $item = $('<li><a href="#" data-title="' + title + '">' + title + '</a></li>');
+ $(options.themeSelectionSelector).append($item);
+ }
+
+ // bind to UI items
+ $(options.themeSelectionSelector).delegate('* > a', 'tap', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ var $a = $(this).closest('a');
+ $a.removeClass('active');
+ switchStyle($a.attr('data-title'));
+
+ // poor-man simulation of radio button behaviour
+ $(options.themeSelectionSelector).find('a').removeClass('selected');
+ $a.addClass('selected');
+ });
+
+ // poor-man simulation of radio button behaviour
+ $(options.themeSelectionSelector).closest('#jqt > *').bind('pageAnimationEnd', function(e, data){
+ if (data.direction === 'in') {
+ $(options.themeSelectionSelector).find('a[data-title="' + current + '"]').addClass('selected');
+ }
+ });
+ }
+
+ return {switchStyle: switchStyle};
+
+ });
+ }
+})($);
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 @@
+/*
+
+ _/ _/_/ _/_/_/_/_/ _/
+ _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/
+ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
+ _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/
+ _/
+ _/
+
+ Created by David Kaneda <http://www.davidkaneda.com>
+ Maintained by Thomas Yip <http://beedesk.com/>
+ Sponsored by Sencha Labs <http://www.sencha.com/>
+ Special thanks to Jonathan Stark <http://www.jonathanstark.com/>
+
+ Documentation and issue tracking on GitHub <http://github.com/senchalabs/jQTouch/>
+
+ (c) 2009-2011 Sencha Labs
+ jQTouch may be freely distributed under the MIT license.
+
+*/
+(function() {
+
+ var fx;
+ if ('Zepto' in window) {
+ fx = window.Zepto;
+ fx.fn.prop = fx.fn.attr;
+
+ Event.prototype.isDefaultPrevented = function() {
+ return this.defaultPrevented;
+ };
+ } else if ('jQuery' in window) {
+ fx = window.jQuery;
+
+ // trick to get Zepto/touch.js to work for jQuery
+ window.Zepto = $;
+ } else {
+ throw('Either Zepto or jQuery is required but neither can be found.');
+ }
+
+ $.jQTouch = function(options) {
+ // Initialize internal jQT variables
+ var $ = fx,
+ $body,
+ $head=$('head'),
+ history=[],
+ newPageCount=0,
+ jQTSettings={},
+ $currentPage='',
+ orientation='portrait',
+ touchSelectors=[],
+ publicObj={},
+ tapBuffer=100, // High click delay = ~350, quickest animation (slide) = 250
+ extensions=$.jQTouch.prototype.extensions,
+ extTapHandlers=$.jQTouch.prototype.tapHandlers,
+ tapHandlers=[],
+ animations=[],
+ hairExtensions='',
+ defaults = {
+ addGlossToIcon: true,
+ backSelector: '.back, .cancel, .goback',
+ cacheGetRequests: true,
+ debug: true,
+ defaultAnimation: 'slideleft',
+ fixedViewport: true,
+ formSelector: 'form',
+ fullScreen: true,
+ fullScreenClass: 'fullscreen',
+ icon: null,
+ icon4: null, // available in iOS 4.2 and later.
+ preloadImages: false,
+ startupScreen: null,
+ statusBar: 'default', // other options: black-translucent, black
+ submitSelector: '.submit',
+ touchSelector: 'a, .touch',
+ trackScrollPositions: true,
+ useAnimations: true,
+ useFastTouch: true,
+ useTouchScroll: true,
+ animations: [ // highest to lowest priority
+ {name:'cubeleft', selector:'.cubeleft, .cube', is3d: true},
+ {name:'cuberight', selector:'.cuberight', is3d: true},
+ {name:'dissolve', selector:'.dissolve'},
+ {name:'fade', selector:'.fade'},
+ {name:'flipleft', selector:'.flipleft, .flip', is3d: true},
+ {name:'flipright', selector:'.flipright', is3d: true},
+ {name:'pop', selector:'.pop', is3d: true},
+ {name:'swapleft', selector:'.swap', is3d: true},
+ {name:'slidedown', selector:'.slidedown'},
+ {name:'slideright', selector:'.slideright'},
+ {name:'slideup', selector:'.slideup'},
+ {name:'slideleft', selector:'.slideleft, .slide, #jqt > * > ul li a'}
+ ]
+ }; // end defaults
+
+ function warn(message) {
+ if (window.console !== undefined && jQTSettings.debug === true) {
+ console.warn(message);
+ }
+ }
+ function addAnimation(animation) {
+ if (typeof(animation.selector) === 'string' && typeof(animation.name) === 'string') {
+ animations.push(animation);
+ }
+ }
+ function addTapHandler(tapHandler) {
+ if (typeof(tapHandler.name) === 'string'
+ && typeof(tapHandler.isSupported) === 'function'
+ && typeof(tapHandler.fn) === 'function') {
+
+ tapHandlers.push(tapHandler);
+ }
+ }
+ function addPageToHistory(page, animation) {
+ history.unshift({
+ page: page,
+ animation: animation,
+ hash: '#' + page.attr('id'),
+ id: page.attr('id')
+ });
+ }
+
+ // Unfortunately, we can not assume the "tap" event
+ // is being used for links, forms, etc.
+ function clickHandler(e) {
+ // Figure out whether to prevent default
+ var $el = $(e.target);
+
+ // Find the nearest tappable ancestor
+ if (!$el.is(touchSelectors.join(', '))) {
+ $el = $(e.target).closest(touchSelectors.join(', '));
+ }
+
+ // Prevent default if we found an internal link (relative or absolute)
+ if ($el && $el.attr('href') && !$el.isExternalLink()) {
+ warn('Need to prevent default click behavior');
+ e.preventDefault();
+ } else {
+ warn('No need to prevent default click behavior');
+ }
+
+ // Trigger a tap event if touchstart is not on the job
+ if ($.support.touch) {
+ warn('Not converting click to a tap event because touch handler is on the job');
+ } else {
+ warn('Converting click event to a tap event because touch handlers are not present or off');
+ $(e.target).trigger('tap', e);
+ }
+
+ }
+ function doNavigation(fromPage, toPage, animation, goingBack) {
+
+ goingBack = goingBack ? goingBack : false;
+
+ // Error check for target page
+ if (toPage === undefined || toPage.length === 0) {
+ $.fn.unselect();
+ warn('Target element is missing.');
+ return false;
+ }
+
+ // Error check for fromPage===toPage
+ if (toPage.hasClass('current')) {
+ $.fn.unselect();
+ warn('You are already on the page you are trying to navigate to.');
+ return false;
+ }
+
+ // Collapse the keyboard
+ $(':focus').trigger('blur');
+
+ fromPage.trigger('pageAnimationStart', { direction: 'out', back: goingBack });
+ toPage.trigger('pageAnimationStart', { direction: 'in', back: goingBack });
+
+ if ($.support.animationEvents && animation && jQTSettings.useAnimations) {
+ // Fail over to 2d animation if need be
+ if (!$.support.transform3d && animation.is3d) {
+ warn('Did not detect support for 3d animations, falling back to ' + jQTSettings.defaultAnimation);
+ animation.name = jQTSettings.defaultAnimation;
+ }
+
+ // Reverse animation if need be
+ var finalAnimationName = animation.name,
+ is3d = animation.is3d ? 'animating3d' : '';
+
+ if (goingBack) {
+ finalAnimationName = finalAnimationName.replace(/left|right|up|down|in|out/, reverseAnimation );
+ }
+
+ warn('finalAnimationName is ' + finalAnimationName);
+
+ // Bind internal "cleanup" callback
+ fromPage.bind('webkitAnimationEnd', navigationEndHandler);
+
+ // Trigger animations
+ $body.addClass('animating ' + is3d);
+
+ var lastScroll = window.pageYOffset;
+
+ // Position the incoming page so toolbar is at top of viewport regardless of scroll position on from page
+ if (jQTSettings.trackScrollPositions === true) {
+ toPage.css('top', window.pageYOffset - (toPage.data('lastScroll') || 0));
+ }
+
+ toPage.addClass(finalAnimationName + ' in current');
+ fromPage.removeClass('current').addClass(finalAnimationName + ' out inmotion');
+
+ if (jQTSettings.trackScrollPositions === true) {
+ fromPage.data('lastScroll', lastScroll);
+ $('.scroll', fromPage).each(function(){
+ $(this).data('lastScroll', this.scrollTop);
+ });
+ }
+ } else {
+ toPage.addClass('current in');
+ fromPage.removeClass('current');
+ navigationEndHandler();
+ }
+
+ // Housekeeping
+ $currentPage = toPage;
+ if (goingBack) {
+ history.shift();
+ } else {
+ addPageToHistory($currentPage, animation);
+ }
+ setHash($currentPage.attr('id'));
+
+ // Private navigationEnd callback
+ function navigationEndHandler(event) {
+ var bufferTime = tapBuffer;
+
+ if ($.support.animationEvents && animation && jQTSettings.useAnimations) {
+ fromPage.unbind('webkitAnimationEnd', navigationEndHandler);
+ fromPage.removeClass(finalAnimationName + ' out inmotion');
+ if (finalAnimationName) {
+ toPage.removeClass(finalAnimationName);
+ }
+ $body.removeClass('animating animating3d');
+ if (jQTSettings.trackScrollPositions === true) {
+ toPage.css('top', -toPage.data('lastScroll'));
+
+ // Have to make sure the scroll/style resets
+ // are outside the flow of this function.
+ setTimeout(function(){
+ toPage.css('top', 0);
+ window.scroll(0, toPage.data('lastScroll'));
+ $('.scroll', toPage).each(function(){
+ this.scrollTop = - $(this).data('lastScroll');
+ });
+ }, 0);
+ }
+ } else {
+ fromPage.removeClass(finalAnimationName + ' out inmotion');
+ if (finalAnimationName) {
+ toPage.removeClass(finalAnimationName);
+ }
+ bufferTime += 260;
+ }
+
+ // In class is intentionally delayed, as it is our ghost click hack
+ setTimeout(function(){
+ toPage.removeClass('in');
+ window.scroll(0,0);
+ }, bufferTime);
+
+ fromPage.unselect();
+
+ // Trigger custom events
+ toPage.trigger('pageAnimationEnd', {
+ direction:'in', animation: animation, back: goingBack
+ });
+ fromPage.trigger('pageAnimationEnd', {
+ direction:'out', animation: animation, back: goingBack
+ });
+ }
+
+ return true;
+ }
+ function reverseAnimation(animation) {
+ var opposites={
+ 'up' : 'down',
+ 'down' : 'up',
+ 'left' : 'right',
+ 'right' : 'left',
+ 'in' : 'out',
+ 'out' : 'in'
+ };
+
+ return opposites[animation] || animation;
+ }
+ function getOrientation() {
+ return orientation;
+ }
+ function goBack() {
+
+ // Error checking
+ if (history.length < 1 ) {
+ warn('History is empty.');
+ }
+
+ if (history.length === 1 ) {
+ warn('You are on the first panel.');
+ window.history.go(-1);
+ }
+
+ var from = history[0],
+ to = history[1];
+
+ if (doNavigation(from.page, to.page, from.animation, true)) {
+ return publicObj;
+ } else {
+ warn('Could not go back.');
+ return false;
+ }
+
+ }
+ function goTo(toPage, animation) {
+
+ var fromPage = history[0].page;
+
+ if (typeof animation === 'string') {
+ for (var i=0, max=animations.length; i < max; i++) {
+ if (animations[i].name === animation) {
+ animation = animations[i];
+ break;
+ }
+ }
+ }
+
+ if (typeof toPage === 'string') {
+ var nextPage = $(toPage);
+
+ if (nextPage.length < 1) {
+ showPageByHref(toPage, {
+ animation: animation
+ });
+ return;
+ } else {
+ toPage = nextPage;
+ }
+ }
+ if (doNavigation(fromPage, toPage, animation)) {
+ return publicObj;
+ } else {
+ warn('Could not animate pages.');
+ return false;
+ }
+ }
+ function hashChangeHandler(e) {
+ if (location.hash === history[0].hash) {
+ warn('We are on the right panel');
+ return true;
+ } else if (location.hash === '') {
+ goBack();
+ return true;
+ } else {
+ if( (history[1] && location.hash === history[1].hash) ) {
+ goBack();
+ return true;
+ } else {
+ // Lastly, just try going to the ID...
+ warn('Could not find ID in history, just forwarding to DOM element.');
+ goTo($(location.hash), jQTSettings.defaultAnimation);
+ }
+ }
+ }
+ function init(options) {
+ jQTSettings = $.extend({}, defaults, options);
+
+ // Preload images
+ if (jQTSettings.preloadImages) {
+ for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--) {
+ (new Image()).src = jQTSettings.preloadImages[i];
+ }
+ }
+
+ // Set appropriate icon (retina display available in iOS 4.2 and later.)
+ var precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed';
+ if (jQTSettings.icon) {
+ hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" href="' + jQTSettings.icon + '" />';
+ }
+ if (jQTSettings.icon4) {
+ hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" sizes="114x114" href="' + jQTSettings.icon4 + '" />';
+ }
+ // Set startup screen
+ if (jQTSettings.startupScreen) {
+ hairExtensions += '<link rel="apple-touch-startup-image" href="' + jQTSettings.startupScreen + '" />';
+ }
+
+ // Set viewport
+ if (jQTSettings.fixedViewport) {
+ hairExtensions += '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>';
+ }
+
+ // Set full-screen
+ if (jQTSettings.fullScreen) {
+ hairExtensions += '<meta name="apple-mobile-web-app-capable" content="yes" />';
+ if (jQTSettings.statusBar) {
+ hairExtensions += '<meta name="apple-mobile-web-app-status-bar-style" content="' + jQTSettings.statusBar + '" />';
+ }
+ }
+
+ // Attach hair extensions
+ if (hairExtensions) {
+ $head.prepend(hairExtensions);
+ }
+ }
+
+ function getAnimation(el) {
+ var animation;
+
+ for (var i=0, max=animations.length; i < max; i++) {
+ if (el.is(animations[i].selector)) {
+ animation = animations[i];
+ break;
+ }
+ }
+
+ if (!animation) {
+ warn('Animation could not be found. Using ' + jQTSettings.defaultAnimation + '.');
+ animation = jQTSettings.defaultAnimation;
+ }
+ return animation;
+ }
+
+ function insertPages(nodes, animation) {
+
+ var targetPage = null;
+
+ // Call dom.createElement element directly instead of relying on $(nodes),
+ // to work around: https://github.com/madrobby/zepto/issues/312
+ var div = document.createElement('div');
+ div.innerHTML = nodes;
+
+ $(div).children().each(function(index, node) {
+ var $node = $(this);
+ if (!$node.attr('id')) {
+ $node.attr('id', 'page-' + (++newPageCount));
+ }
+
+ // Remove any existing instance
+ $('#' + $node.attr('id')).remove();
+
+ $body.append($node);
+ $body.trigger('pageInserted', {page: $node});
+
+ if ($node.hasClass('current') || !targetPage) {
+ targetPage = $node;
+ }
+ });
+ if (targetPage !== null) {
+ goTo(targetPage, animation);
+ return targetPage;
+ } else {
+ return false;
+ }
+ }
+
+ function orientationChangeHandler() {
+ $body.css('minHeight', 1000);
+ scrollTo(0,0);
+ var bodyHeight = window.innerHeight;
+ $body.css('minHeight', bodyHeight);
+
+ orientation = Math.abs(window.orientation) == 90 ? 'landscape' : 'portrait';
+ $body.removeClass('portrait landscape').addClass(orientation).trigger('turn', {orientation: orientation});
+ }
+ function setHash(hash) {
+ // Sanitize
+ location.hash = '#' + hash.replace(/^#/, '');
+ }
+ function showPageByHref(href, options) {
+
+ var defaults = {
+ data: null,
+ method: 'GET',
+ animation: null,
+ callback: null,
+ $referrer: null
+ };
+
+ var settings = $.extend({}, defaults, options);
+
+ if (href != '#') {
+ $.ajax({
+ url: href,
+ data: settings.data,
+ type: settings.method,
+ success: function (data) {
+ var firstPage = insertPages(data, settings.animation);
+ if (firstPage) {
+ if (settings.method == 'GET' && jQTSettings.cacheGetRequests === true && settings.$referrer) {
+ settings.$referrer.attr('href', '#' + firstPage.attr('id'));
+ }
+ if (settings.callback) {
+ settings.callback(true);
+ }
+ }
+ },
+ error: function (data) {
+ if (settings.$referrer) {
+ settings.$referrer.unselect();
+ }
+ if (settings.callback) {
+ settings.callback(false);
+ }
+ }
+ });
+ } else if (settings.$referrer) {
+ settings.$referrer.unselect();
+ }
+ }
+ function submitHandler(e, callback) {
+
+ $(':focus').trigger('blur');
+
+ e.preventDefault();
+
+ var $form = (typeof(e)==='string') ? $(e).eq(0) : (e.target ? $(e.target) : $(e));
+
+ if ($form.length && $form.is(jQTSettings.formSelector) && $form.attr('action')) {
+ showPageByHref($form.attr('action'), {
+ data: $form.serialize(),
+ method: $form.attr('method') || "POST",
+ animation: getAnimation($form),
+ callback: callback
+ });
+ return false;
+ }
+ return true;
+ }
+ function submitParentForm($el) {
+
+ var $form = $el.closest('form');
+ if ($form.length === 0) {
+ warn('No parent form found');
+ } else {
+ warn('About to submit parent form');
+ $form.trigger('submit');
+ return false;
+ }
+ return true;
+ }
+ function supportForTransform3d() {
+
+ var head, body, style, div, result;
+
+ head = document.getElementsByTagName('head')[0];
+ body = document.body;
+
+ style = document.createElement('style');
+ style.textContent = '@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-webkit-transform-3d){#jqt-3dtest{height:3px}}';
+
+ div = document.createElement('div');
+ div.id = 'jqt-3dtest';
+
+ // Add to the page
+ head.appendChild(style);
+ body.appendChild(div);
+
+ // Check the result
+ result = div.offsetHeight === 3;
+
+ // Clean up
+ style.parentNode.removeChild(style);
+ div.parentNode.removeChild(div);
+
+ // Pass back result
+ warn('Support for 3d transforms: ' + result);
+ return result;
+ }
+ function supportIOS5() {
+ var support = false;
+ var REGEX_IOS_VERSION = /OS (\d+)(_\d+)* like Mac OS X/i;
+
+ var agentString = window.navigator.userAgent;
+ if (REGEX_IOS_VERSION.test(agentString)) {
+ support = (REGEX_IOS_VERSION.exec(agentString)[1] >= 5);
+ }
+ return support;
+ }
+ function touchStartHandler(e){
+
+ var $el = $(e.target),
+ selectors = touchSelectors.join(', ');
+
+ // Find the nearest tappable ancestor
+ if (!$el.is(selectors)) {
+ $el = $el.closest(selectors);
+ }
+
+ // Make sure we have a tappable element
+ if ($el.length && $el.attr('href')) {
+ $el.addClass('active');
+ }
+
+ // Remove our active class if we move
+ $el.on($.support.touch ? 'touchmove' : 'mousemove', function(){
+ $el.removeClass('active');
+ });
+
+ $el.on('touchend', function(){
+ $el.unbind('touchmove mousemove');
+ });
+
+ }
+ function tapHandler(e){
+
+ if (e.isDefaultPrevented()) {
+ return true;
+ }
+
+ // Grab the target element
+ var $el = $(e.target);
+
+ // Find the nearest tappable ancestor
+ if (!$el.is(touchSelectors.join(', '))) {
+ $el = $el.closest(touchSelectors.join(', '));
+ }
+
+ // Make sure we have a tappable element
+ if (!$el.length || !$el.attr('href')) {
+ warn('Could not find a link related to tapped element');
+ return true;
+ }
+
+ // Init some vars
+ var target = $el.attr('target'),
+ hash = $el.prop('hash'),
+ href = $el.attr('href');
+
+ var params = {
+ e: e,
+ $el: $el,
+ target: target,
+ hash: hash,
+ href: href,
+ jQTSettings: jQTSettings
+ };
+
+ // Loop thru all handlers
+ for (var i=0, len=tapHandlers.length; i<len; i++) {
+ var handler = tapHandlers[i];
+ var supported = handler.isSupported(e, params);
+ if (supported) {
+ var flag = handler.fn(e, params);
+ return flag;
+ }
+ }
+ }
+ function addDefaultTapHandlers() {
+ addTapHandler({
+ name: 'external-link',
+ isSupported: function(e, params) {
+ return params.$el.isExternalLink();
+ },
+ fn: function(e, params) {
+ params.$el.unselect();
+ return true;
+ }
+ });
+ addTapHandler({
+ name: 'back-selector',
+ isSupported: function(e, params) {
+ return params.$el.is(params.jQTSettings.backSelector);
+ },
+ fn: function(e, params) {
+ // User clicked or tapped a back button
+ goBack(params.hash);
+ }
+ });
+ addTapHandler({
+ name: 'submit-selector',
+ isSupported: function(e, params) {
+ return params.$el.is(params.jQTSettings.submitSelector);
+ },
+ fn: function(e, params) {
+ // User clicked or tapped a submit element
+ submitParentForm(params.$el);
+ }
+ });
+ addTapHandler({
+ name: 'webapp',
+ isSupported: function(e, params) {
+ return params.target === '_webapp';
+ },
+ fn: function(e, params) {
+ // User clicked or tapped an internal link, fullscreen mode
+ window.location = params.href;
+ return false;
+ }
+ });
+ addTapHandler({
+ name: 'no-op',
+ isSupported: function(e, params) {
+ return params.href === '#';
+ },
+ fn: function(e, params) {
+ // Allow tap on item with no href
+ params.$el.unselect();
+ return true;
+ }
+ });
+ addTapHandler({
+ name: 'standard',
+ isSupported: function(e, params) {
+ return params.hash && params.hash !== '#';
+ },
+ fn: function(e, params) {
+ var animation = getAnimation(params.$el);
+ // Internal href
+ params.$el.addClass('active');
+ goTo(
+ $(params.hash).data('referrer', params.$el),
+ animation,
+ params.$el.hasClass('reverse')
+ );
+ return false;
+ }
+ });
+ addTapHandler({
+ name: 'external',
+ isSupported: function(e, params) {
+ return true;
+ },
+ fn: function(e, params) {
+ var animation = getAnimation(params.$el);
+
+ // External href
+ params.$el.addClass('loading active');
+ showPageByHref(params.$el.attr('href'), {
+ animation: animation,
+ callback: function() {
+ params.$el.removeClass('loading');
+ setTimeout($.fn.unselect, 250, params.$el);
+ },
+ $referrer: params.$el
+ });
+ return false;
+ }
+ });
+ };
+
+ // Get the party started
+ init(options);
+
+ // Document ready stuff
+ $(document).ready(function RUMBLE() {
+ // Store some properties in a support object
+ if (!$.support) $.support = {};
+ $.support.animationEvents = (typeof window.WebKitAnimationEvent != 'undefined');
+ $.support.touch = (typeof window.TouchEvent != 'undefined') && (window.navigator.userAgent.indexOf('Mobile') > -1) && jQTSettings.useFastTouch;
+ $.support.transform3d = supportForTransform3d();
+ $.support.ios5 = supportIOS5();
+
+ if (!$.support.touch) {
+ warn('This device does not support touch interaction, or it has been deactivated by the developer. Some features might be unavailable.');
+ }
+ if (!$.support.transform3d) {
+ warn('This device does not support 3d animation. 2d animations will be used instead.');
+ }
+
+ // Define public jQuery functions
+ $.fn.isExternalLink = function() {
+ var $el = $(this);
+ 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"]'));
+ };
+ $.fn.makeActive = function() {
+ return $(this).addClass('active');
+ };
+ $.fn.unselect = function(obj) {
+ if (obj) {
+ obj.removeClass('active');
+ } else {
+ $('.active').removeClass('active');
+ }
+ };
+
+ // Add extensions
+ for (var i=0, max=extensions.length; i < max; i++) {
+ var fn = extensions[i];
+ if ($.isFunction(fn)) {
+ $.extend(publicObj, fn(publicObj));
+ }
+ }
+
+ // Add extensions tapHandlers
+ for (var i=0, max=extTapHandlers.length; i < max; i++) {
+ addTapHandler(extTapHandlers[i]);
+ }
+ // Add default tapHandlers
+ addDefaultTapHandlers();
+
+ // Add animations
+ for (var j=0, max_anims=defaults.animations.length; j < max_anims; j++) {
+ var animation = defaults.animations[j];
+ if(jQTSettings[animation.name + 'Selector'] !== undefined){
+ animation.selector = jQTSettings[animation.name + 'Selector'];
+ }
+ addAnimation(animation);
+ }
+
+ // Create an array of stuff that needs touch event handling
+ touchSelectors.push(jQTSettings.touchSelector);
+ touchSelectors.push(jQTSettings.backSelector);
+ touchSelectors.push(jQTSettings.submitSelector);
+ $(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none');
+
+ // Make sure we have a jqt element
+ $body = $('#jqt');
+ var anatomy_lessons = [];
+
+ if ($body.length === 0) {
+ 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.');
+ $body = $(document.body).attr('id', 'jqt');
+ }
+
+ // Add some specific css if need be
+ if ($.support.transform3d) {
+ anatomy_lessons.push('supports3d');
+ }
+ if ($.support.ios5 && jQTSettings.useTouchScroll) {
+ anatomy_lessons.push('touchscroll');
+ }
+
+ if (jQTSettings.fullScreenClass && window.navigator.standalone === true) {
+ anatomy_lessons.push(jQTSettings.fullScreenClass, jQTSettings.statusBar);
+ }
+
+ // Bind events
+
+ $body
+ .addClass(anatomy_lessons.join(' '))
+ .bind('click', clickHandler)
+ .bind('orientationchange', orientationChangeHandler)
+ .bind('submit', submitHandler)
+ .bind('tap', tapHandler)
+ .bind( $.support.touch ? 'touchstart' : 'mousedown', touchStartHandler)
+ .trigger('orientationchange');
+
+ $(window).bind('hashchange', hashChangeHandler);
+
+ var startHash = location.hash;
+
+ // Determine what the initial view should be
+ if ($('#jqt > .current').length === 0) {
+ $currentPage = $('#jqt > *:first-child').addClass('current');
+ } else {
+ $currentPage = $('#jqt > .current');
+ }
+
+ setHash($currentPage.attr('id'));
+ addPageToHistory($currentPage);
+
+ if ($(startHash).length === 1) {
+ goTo(startHash);
+ }
+ });
+
+ // Expose public methods and properties
+ publicObj = {
+ addAnimation: addAnimation,
+ animations: animations,
+ getOrientation: getOrientation,
+ goBack: goBack,
+ insertPages: insertPages,
+ goTo: goTo,
+ history: history,
+ settings: jQTSettings,
+ submitForm: submitHandler
+ };
+ return publicObj;
+ };
+
+ $.jQTouch.prototype.extensions = [];
+ $.jQTouch.prototype.tapHandlers = [];
+
+ // Extensions directly manipulate the jQTouch object, before it's initialized.
+ $.jQTouch.addExtension = function(extension) {
+ $.jQTouch.prototype.extensions.push(extension);
+ };
+
+ // Experimental tap hanlders that can bypass default jQTouch tap handling
+ $.jQTouch.addTapHandler = function(extension) {
+ $.jQTouch.prototype.tapHandlers.push(extension);
+ };
+
+})(); // 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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ var jsonpID = 0,
+ isObject = $.isObject,
+ document = window.document,
+ key,
+ name,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ scriptTypeRE = /^(?:text|application)\/javascript/i,
+ xmlTypeRE = /^(?:text|application)\/xml/i,
+ jsonType = 'application/json',
+ htmlType = 'text/html',
+ blankRE = /^\s*$/
+
+ // trigger a custom event and return false if it was cancelled
+ function triggerAndReturn(context, eventName, data) {
+ var event = $.Event(eventName)
+ $(context).trigger(event, data)
+ return !event.defaultPrevented
+ }
+
+ // trigger an Ajax "global" event
+ function triggerGlobal(settings, context, eventName, data) {
+ if (settings.global) return triggerAndReturn(context || document, eventName, data)
+ }
+
+ // Number of active Ajax requests
+ $.active = 0
+
+ function ajaxStart(settings) {
+ if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
+ }
+ function ajaxStop(settings) {
+ if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
+ }
+
+ // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
+ function ajaxBeforeSend(xhr, settings) {
+ var context = settings.context
+ if (settings.beforeSend.call(context, xhr, settings) === false ||
+ triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
+ return false
+
+ triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
+ }
+ function ajaxSuccess(data, xhr, settings) {
+ var context = settings.context, status = 'success'
+ settings.success.call(context, data, status, xhr)
+ triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
+ ajaxComplete(status, xhr, settings)
+ }
+ // type: "timeout", "error", "abort", "parsererror"
+ function ajaxError(error, type, xhr, settings) {
+ var context = settings.context
+ settings.error.call(context, xhr, type, error)
+ triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error])
+ ajaxComplete(type, xhr, settings)
+ }
+ // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
+ function ajaxComplete(status, xhr, settings) {
+ var context = settings.context
+ settings.complete.call(context, xhr, status)
+ triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
+ ajaxStop(settings)
+ }
+
+ // Empty function, used as default callback
+ function empty() {}
+
+ $.ajaxJSONP = function(options){
+ if (!('type' in options)) return $.ajax(options)
+
+ var callbackName = 'jsonp' + (++jsonpID),
+ script = document.createElement('script'),
+ abort = function(){
+ $(script).remove()
+ if (callbackName in window) window[callbackName] = empty
+ ajaxComplete('abort', xhr, options)
+ },
+ xhr = { abort: abort }, abortTimeout
+
+ if (options.error) script.onerror = function() {
+ xhr.abort()
+ options.error()
+ }
+
+ window[callbackName] = function(data){
+ clearTimeout(abortTimeout)
+ $(script).remove()
+ delete window[callbackName]
+ ajaxSuccess(data, xhr, options)
+ }
+
+ serializeData(options)
+ script.src = options.url.replace(/=\?/, '=' + callbackName)
+ $('head').append(script)
+
+ if (options.timeout > 0) abortTimeout = setTimeout(function(){
+ xhr.abort()
+ ajaxComplete('timeout', xhr, options)
+ }, options.timeout)
+
+ return xhr
+ }
+
+ $.ajaxSettings = {
+ // Default type of request
+ type: 'GET',
+ // Callback that is executed before request
+ beforeSend: empty,
+ // Callback that is executed if the request succeeds
+ success: empty,
+ // Callback that is executed the the server drops error
+ error: empty,
+ // Callback that is executed on request complete (both: error and success)
+ complete: empty,
+ // The context for the callbacks
+ context: null,
+ // Whether to trigger "global" Ajax events
+ global: true,
+ // Transport
+ xhr: function () {
+ return new window.XMLHttpRequest()
+ },
+ // MIME types mapping
+ accepts: {
+ script: 'text/javascript, application/javascript',
+ json: jsonType,
+ xml: 'application/xml, text/xml',
+ html: htmlType,
+ text: 'text/plain'
+ },
+ // Whether the request is to another domain
+ crossDomain: false,
+ // Default timeout
+ timeout: 0,
+ // Whether data should be serialized to string
+ processData: true
+ }
+
+ function mimeToDataType(mime) {
+ return mime && ( mime == htmlType ? 'html' :
+ mime == jsonType ? 'json' :
+ scriptTypeRE.test(mime) ? 'script' :
+ xmlTypeRE.test(mime) && 'xml' ) || 'text'
+ }
+
+ function appendQuery(url, query) {
+ return (url + '&' + query).replace(/[&?]{1,2}/, '?')
+ }
+
+ // serialize payload and append it to the URL for GET requests
+ function serializeData(options) {
+ if (options.processData && isObject(options.data))
+ options.data = $.param(options.data, options.traditional)
+ if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
+ options.url = appendQuery(options.url, options.data)
+ }
+
+ $.ajax = function(options){
+ var settings = $.extend({}, options || {})
+ for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
+
+ ajaxStart(settings)
+
+ if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
+ RegExp.$2 != window.location.host
+
+ var dataType = settings.dataType, hasPlaceholder = /=\?/.test(settings.url)
+ if (dataType == 'jsonp' || hasPlaceholder) {
+ if (!hasPlaceholder) settings.url = appendQuery(settings.url, 'callback=?')
+ return $.ajaxJSONP(settings)
+ }
+
+ if (!settings.url) settings.url = window.location.toString()
+ serializeData(settings)
+
+ var mime = settings.accepts[dataType],
+ baseHeaders = { },
+ protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
+ xhr = $.ajaxSettings.xhr(), abortTimeout
+
+ if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest'
+ if (mime) {
+ baseHeaders['Accept'] = mime
+ if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
+ xhr.overrideMimeType && xhr.overrideMimeType(mime)
+ }
+ if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
+ baseHeaders['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded')
+ settings.headers = $.extend(baseHeaders, settings.headers || {})
+
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4) {
+ xhr.onreadystatechange = empty;
+ clearTimeout(abortTimeout)
+ var result, error = false
+ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
+ dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type'))
+ result = xhr.responseText
+
+ try {
+ if (dataType == 'script') (1,eval)(result)
+ else if (dataType == 'xml') result = xhr.responseXML
+ else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
+ } catch (e) { error = e }
+
+ if (error) ajaxError(error, 'parsererror', xhr, settings)
+ else ajaxSuccess(result, xhr, settings)
+ } else {
+ ajaxError(null, xhr.status ? 'error' : 'abort', xhr, settings)
+ }
+ }
+ }
+
+ var async = 'async' in settings ? settings.async : true
+ xhr.open(settings.type, settings.url, async)
+
+ for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name])
+
+ if (ajaxBeforeSend(xhr, settings) === false) {
+ xhr.abort()
+ return false
+ }
+
+ if (settings.timeout > 0) abortTimeout = setTimeout(function(){
+ xhr.onreadystatechange = empty
+ xhr.abort()
+ ajaxError(null, 'timeout', xhr, settings)
+ }, settings.timeout)
+
+ // avoid sending empty string (#319)
+ xhr.send(settings.data ? settings.data : null)
+ return xhr
+ }
+
+ $.get = function(url, success){ return $.ajax({ url: url, success: success }) }
+
+ $.post = function(url, data, success, dataType){
+ if ($.isFunction(data)) dataType = dataType || success, success = data, data = null
+ return $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType })
+ }
+
+ $.getJSON = function(url, success){
+ return $.ajax({ url: url, success: success, dataType: 'json' })
+ }
+
+ $.fn.load = function(url, success){
+ if (!this.length) return this
+ var self = this, parts = url.split(/\s/), selector
+ if (parts.length > 1) url = parts[0], selector = parts[1]
+ $.get(url, function(response){
+ self.html(selector ?
+ $('<div>').html(response.replace(rscript, "")).find(selector)
+ : response)
+ success && success.apply(self, arguments)
+ })
+ return this
+ }
+
+ var escape = encodeURIComponent
+
+ function serialize(params, obj, traditional, scope){
+ var array = $.isArray(obj)
+ $.each(obj, function(key, value) {
+ if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']'
+ // handle data in serializeArray() format
+ if (!scope && array) params.add(value.name, value.value)
+ // recurse into nested objects
+ else if (traditional ? $.isArray(value) : isObject(value))
+ serialize(params, value, traditional, key)
+ else params.add(key, value)
+ })
+ }
+
+ $.param = function(obj, traditional){
+ var params = []
+ params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }
+ serialize(params, obj, traditional)
+ return params.join('&').replace(/%20/g, '+')
+ }
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ var cache = [], timeout
+
+ $.fn.remove = function(){
+ return this.each(function(){
+ if(this.parentNode){
+ if(this.tagName === 'IMG'){
+ cache.push(this)
+ this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
+ if (timeout) clearTimeout(timeout)
+ timeout = setTimeout(function(){ cache = [] }, 60000)
+ }
+ this.parentNode.removeChild(this)
+ }
+ })
+ }
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+// The following code is heavily inspired by jQuery's $.fn.data()
+
+;(function($) {
+ var data = {}, dataAttr = $.fn.data, camelize = $.camelCase,
+ exp = $.expando = 'Zepto' + (+new Date())
+
+ // Get value from node:
+ // 1. first try key as given,
+ // 2. then try camelized key,
+ // 3. fall back to reading "data-*" attribute.
+ function getData(node, name) {
+ var id = node[exp], store = id && data[id]
+ if (name === undefined) return store || setData(node)
+ else {
+ if (store) {
+ if (name in store) return store[name]
+ var camelName = camelize(name)
+ if (camelName in store) return store[camelName]
+ }
+ return dataAttr.call($(node), name)
+ }
+ }
+
+ // Store value under camelized key on node
+ function setData(node, name, value) {
+ var id = node[exp] || (node[exp] = ++$.uuid),
+ store = data[id] || (data[id] = attributeData(node))
+ if (name !== undefined) store[camelize(name)] = value
+ return store
+ }
+
+ // Read all "data-*" attributes from a node
+ function attributeData(node) {
+ var store = {}
+ $.each(node.attributes, function(i, attr){
+ if (attr.name.indexOf('data-') == 0)
+ store[camelize(attr.name.replace('data-', ''))] =
+ $.zepto.deserializeValue(attr.value)
+ })
+ return store
+ }
+
+ $.fn.data = function(name, value) {
+ return value === undefined ?
+ // set multiple values via object
+ $.isPlainObject(name) ?
+ this.each(function(i, node){
+ $.each(name, function(key, value){ setData(node, key, value) })
+ }) :
+ // get value from first element
+ this.length == 0 ? undefined : getData(this[0], name) :
+ // set value on all elements
+ this.each(function(){ setData(this, name, value) })
+ }
+
+ $.fn.removeData = function(names) {
+ if (typeof names == 'string') names = names.split(/\s+/)
+ return this.each(function(){
+ var id = this[exp], store = id && data[id]
+ if (store) $.each(names, function(){ delete store[camelize(this)] })
+ })
+ }
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ function detect(ua){
+ var os = this.os = {}, browser = this.browser = {},
+ webkit = ua.match(/WebKit\/([\d.]+)/),
+ android = ua.match(/(Android)\s+([\d.]+)/),
+ ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
+ iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
+ webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
+ touchpad = webos && ua.match(/TouchPad/),
+ kindle = ua.match(/Kindle\/([\d.]+)/),
+ silk = ua.match(/Silk\/([\d._]+)/),
+ blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/),
+ chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/)
+
+ // todo clean this up with a better OS/browser
+ // separation. we need to discern between multiple
+ // browsers on android, and decide if kindle fire in
+ // silk mode is android or not
+
+ if (browser.webkit = !!webkit) browser.version = webkit[1]
+
+ if (android) os.android = true, os.version = android[2]
+ if (iphone) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.')
+ if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.')
+ if (webos) os.webos = true, os.version = webos[2]
+ if (touchpad) os.touchpad = true
+ if (blackberry) os.blackberry = true, os.version = blackberry[2]
+ if (kindle) os.kindle = true, os.version = kindle[1]
+ if (silk) browser.silk = true, browser.version = silk[1]
+ if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true
+ if (chrome) browser.chrome = true, browser.version = chrome[1]
+ }
+
+ detect.call($, navigator.userAgent)
+ // make available to unit tests
+ $.__detect = detect
+
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={},
+ hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
+
+ specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
+
+ function zid(element) {
+ return element._zid || (element._zid = _zid++)
+ }
+ function findHandlers(element, event, fn, selector) {
+ event = parse(event)
+ if (event.ns) var matcher = matcherFor(event.ns)
+ return (handlers[zid(element)] || []).filter(function(handler) {
+ return handler
+ && (!event.e || handler.e == event.e)
+ && (!event.ns || matcher.test(handler.ns))
+ && (!fn || zid(handler.fn) === zid(fn))
+ && (!selector || handler.sel == selector)
+ })
+ }
+ function parse(event) {
+ var parts = ('' + event).split('.')
+ return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
+ }
+ function matcherFor(ns) {
+ return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
+ }
+
+ function eachEvent(events, fn, iterator){
+ if ($.isObject(events)) $.each(events, iterator)
+ else events.split(/\s/).forEach(function(type){ iterator(type, fn) })
+ }
+
+ function eventCapture(handler, captureSetting) {
+ return handler.del &&
+ (handler.e == 'focus' || handler.e == 'blur') ||
+ !!captureSetting
+ }
+
+ function realEvent(type) {
+ return hover[type] || type
+ }
+
+ function add(element, events, fn, selector, getDelegate, capture){
+ var id = zid(element), set = (handlers[id] || (handlers[id] = []))
+ eachEvent(events, fn, function(event, fn){
+ var handler = parse(event)
+ handler.fn = fn
+ handler.sel = selector
+ // emulate mouseenter, mouseleave
+ if (handler.e in hover) fn = function(e){
+ var related = e.relatedTarget
+ if (!related || (related !== this && !$.contains(this, related)))
+ return handler.fn.apply(this, arguments)
+ }
+ handler.del = getDelegate && getDelegate(fn, event)
+ var callback = handler.del || fn
+ handler.proxy = function (e) {
+ var result = callback.apply(element, [e].concat(e.data))
+ if (result === false) e.preventDefault(), e.stopPropagation()
+ return result
+ }
+ handler.i = set.length
+ set.push(handler)
+ element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
+ })
+ }
+ function remove(element, events, fn, selector, capture){
+ var id = zid(element)
+ eachEvent(events || '', fn, function(event, fn){
+ findHandlers(element, event, fn, selector).forEach(function(handler){
+ delete handlers[id][handler.i]
+ element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
+ })
+ })
+ }
+
+ $.event = { add: add, remove: remove }
+
+ $.proxy = function(fn, context) {
+ if ($.isFunction(fn)) {
+ var proxyFn = function(){ return fn.apply(context, arguments) }
+ proxyFn._zid = zid(fn)
+ return proxyFn
+ } else if (typeof context == 'string') {
+ return $.proxy(fn[context], fn)
+ } else {
+ throw new TypeError("expected function")
+ }
+ }
+
+ $.fn.bind = function(event, callback){
+ return this.each(function(){
+ add(this, event, callback)
+ })
+ }
+ $.fn.unbind = function(event, callback){
+ return this.each(function(){
+ remove(this, event, callback)
+ })
+ }
+ $.fn.one = function(event, callback){
+ return this.each(function(i, element){
+ add(this, event, callback, null, function(fn, type){
+ return function(){
+ var result = fn.apply(element, arguments)
+ remove(element, type, fn)
+ return result
+ }
+ })
+ })
+ }
+
+ var returnTrue = function(){return true},
+ returnFalse = function(){return false},
+ ignoreProperties = /^([A-Z]|layer[XY]$)/,
+ eventMethods = {
+ preventDefault: 'isDefaultPrevented',
+ stopImmediatePropagation: 'isImmediatePropagationStopped',
+ stopPropagation: 'isPropagationStopped'
+ }
+ function createProxy(event) {
+ var key, proxy = { originalEvent: event }
+ for (key in event)
+ if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
+
+ $.each(eventMethods, function(name, predicate) {
+ proxy[name] = function(){
+ this[predicate] = returnTrue
+ return event[name].apply(event, arguments)
+ }
+ proxy[predicate] = returnFalse
+ })
+ return proxy
+ }
+
+ // emulates the 'defaultPrevented' property for browsers that have none
+ function fix(event) {
+ if (!('defaultPrevented' in event)) {
+ event.defaultPrevented = false
+ var prevent = event.preventDefault
+ event.preventDefault = function() {
+ this.defaultPrevented = true
+ prevent.call(this)
+ }
+ }
+ }
+
+ $.fn.delegate = function(selector, event, callback){
+ return this.each(function(i, element){
+ add(element, event, callback, selector, function(fn){
+ return function(e){
+ var evt, match = $(e.target).closest(selector, element).get(0)
+ if (match) {
+ evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
+ return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
+ }
+ }
+ })
+ })
+ }
+ $.fn.undelegate = function(selector, event, callback){
+ return this.each(function(){
+ remove(this, event, callback, selector)
+ })
+ }
+
+ $.fn.live = function(event, callback){
+ $(document.body).delegate(this.selector, event, callback)
+ return this
+ }
+ $.fn.die = function(event, callback){
+ $(document.body).undelegate(this.selector, event, callback)
+ return this
+ }
+
+ $.fn.on = function(event, selector, callback){
+ return !selector || $.isFunction(selector) ?
+ this.bind(event, selector || callback) : this.delegate(selector, event, callback)
+ }
+ $.fn.off = function(event, selector, callback){
+ return !selector || $.isFunction(selector) ?
+ this.unbind(event, selector || callback) : this.undelegate(selector, event, callback)
+ }
+
+ $.fn.trigger = function(event, data){
+ if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
+ fix(event)
+ event.data = data
+ return this.each(function(){
+ // items in the collection might not be DOM elements
+ // (todo: possibly support events on plain old objects)
+ if('dispatchEvent' in this) this.dispatchEvent(event)
+ })
+ }
+
+ // triggers event handlers on current element just as if an event occurred,
+ // doesn't trigger an actual event, doesn't bubble
+ $.fn.triggerHandler = function(event, data){
+ var e, result
+ this.each(function(i, element){
+ e = createProxy(typeof event == 'string' ? $.Event(event) : event)
+ e.data = data
+ e.target = element
+ $.each(findHandlers(element, event.type || event), function(i, handler){
+ result = handler.proxy(e)
+ if (e.isImmediatePropagationStopped()) return false
+ })
+ })
+ return result
+ }
+
+ // shortcut methods for `.bind(event, fn)` for each event type
+ ;('focusin focusout load resize scroll unload click dblclick '+
+ 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
+ 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
+ $.fn[event] = function(callback) {
+ return callback ?
+ this.bind(event, callback) :
+ this.trigger(event)
+ }
+ })
+
+ ;['focus', 'blur'].forEach(function(name) {
+ $.fn[name] = function(callback) {
+ if (callback) this.bind(name, callback)
+ else this.each(function(){
+ try { this[name]() }
+ catch(e) {}
+ })
+ return this
+ }
+ })
+
+ $.Event = function(type, props) {
+ if (typeof type != 'string') props = type, type = props.type
+ var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
+ if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
+ event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
+ event.isDefaultPrevented = function(){ return this.defaultPrevented }
+ return event
+ }
+
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function ($) {
+ $.fn.serializeArray = function () {
+ var result = [], el
+ $( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
+ el = $(this)
+ var type = el.attr('type')
+ if (this.nodeName.toLowerCase() != 'fieldset' &&
+ !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
+ ((type != 'radio' && type != 'checkbox') || this.checked))
+ result.push({
+ name: el.attr('name'),
+ value: el.val()
+ })
+ })
+ return result
+ }
+
+ $.fn.serialize = function () {
+ var result = []
+ this.serializeArray().forEach(function (elm) {
+ result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) )
+ })
+ return result.join('&')
+ }
+
+ $.fn.submit = function (callback) {
+ if (callback) this.bind('submit', callback)
+ else if (this.length) {
+ var event = $.Event('submit')
+ this.eq(0).trigger(event)
+ if (!event.defaultPrevented) this.get(0).submit()
+ }
+ return this
+ }
+
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($, undefined){
+ var prefix = '', eventPrefix, endEventName, endAnimationName,
+ vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
+ document = window.document, testEl = document.createElement('div'),
+ supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
+ transform,
+ transitionProperty, transitionDuration, transitionTiming,
+ animationName, animationDuration, animationTiming,
+ cssReset = {}
+
+ function dasherize(str) { return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2')) }
+ function downcase(str) { return str.toLowerCase() }
+ function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }
+
+ $.each(vendors, function(vendor, event){
+ if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
+ prefix = '-' + downcase(vendor) + '-'
+ eventPrefix = event
+ return false
+ }
+ })
+
+ transform = prefix + 'transform'
+ cssReset[transitionProperty = prefix + 'transition-property'] =
+ cssReset[transitionDuration = prefix + 'transition-duration'] =
+ cssReset[transitionTiming = prefix + 'transition-timing-function'] =
+ cssReset[animationName = prefix + 'animation-name'] =
+ cssReset[animationDuration = prefix + 'animation-duration'] =
+ cssReset[animationTiming = prefix + 'animation-timing-function'] = ''
+
+ $.fx = {
+ off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
+ speeds: { _default: 400, fast: 200, slow: 600 },
+ cssPrefix: prefix,
+ transitionEnd: normalizeEvent('TransitionEnd'),
+ animationEnd: normalizeEvent('AnimationEnd')
+ }
+
+ $.fn.animate = function(properties, duration, ease, callback){
+ if ($.isObject(duration))
+ ease = duration.easing, callback = duration.complete, duration = duration.duration
+ if (duration) duration = (typeof duration == 'number' ? duration :
+ ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
+ return this.anim(properties, duration, ease, callback)
+ }
+
+ $.fn.anim = function(properties, duration, ease, callback){
+ var key, cssValues = {}, cssProperties, transforms = '',
+ that = this, wrappedCallback, endEvent = $.fx.transitionEnd
+
+ if (duration === undefined) duration = 0.4
+ if ($.fx.off) duration = 0
+
+ if (typeof properties == 'string') {
+ // keyframe animation
+ cssValues[animationName] = properties
+ cssValues[animationDuration] = duration + 's'
+ cssValues[animationTiming] = (ease || 'linear')
+ endEvent = $.fx.animationEnd
+ } else {
+ cssProperties = []
+ // CSS transitions
+ for (key in properties)
+ if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
+ else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
+
+ if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
+ if (duration > 0 && typeof properties === 'object') {
+ cssValues[transitionProperty] = cssProperties.join(', ')
+ cssValues[transitionDuration] = duration + 's'
+ cssValues[transitionTiming] = (ease || 'linear')
+ }
+ }
+
+ wrappedCallback = function(event){
+ if (typeof event !== 'undefined') {
+ if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
+ $(event.target).unbind(endEvent, arguments.callee)
+ }
+ $(this).css(cssReset)
+ callback && callback.call(this)
+ }
+ if (duration > 0) this.bind(endEvent, wrappedCallback)
+
+ // trigger page reflow so new elements can animate
+ this.size() && this.get(0).clientLeft
+
+ this.css(cssValues)
+
+ if (duration <= 0) setTimeout(function() {
+ that.each(function(){ wrappedCallback.call(this) })
+ }, 0)
+
+ return this
+ }
+
+ testEl = null
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($, undefined){
+ var document = window.document, docElem = document.documentElement,
+ origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle
+
+ function anim(el, speed, opacity, scale, callback) {
+ if (typeof speed == 'function' && !callback) callback = speed, speed = undefined
+ var props = { opacity: opacity }
+ if (scale) {
+ props.scale = scale
+ el.css($.fx.cssPrefix + 'transform-origin', '0 0')
+ }
+ return el.animate(props, speed, null, callback)
+ }
+
+ function hide(el, speed, scale, callback) {
+ return anim(el, speed, 0, scale, function(){
+ origHide.call($(this))
+ callback && callback.call(this)
+ })
+ }
+
+ $.fn.show = function(speed, callback) {
+ origShow.call(this)
+ if (speed === undefined) speed = 0
+ else this.css('opacity', 0)
+ return anim(this, speed, 1, '1,1', callback)
+ }
+
+ $.fn.hide = function(speed, callback) {
+ if (speed === undefined) return origHide.call(this)
+ else return hide(this, speed, '0,0', callback)
+ }
+
+ $.fn.toggle = function(speed, callback) {
+ if (speed === undefined || typeof speed == 'boolean')
+ return origToggle.call(this, speed)
+ else return this.each(function(){
+ var el = $(this)
+ el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback)
+ })
+ }
+
+ $.fn.fadeTo = function(speed, opacity, callback) {
+ return anim(this, speed, opacity, null, callback)
+ }
+
+ $.fn.fadeIn = function(speed, callback) {
+ var target = this.css('opacity')
+ if (target > 0) this.css('opacity', 0)
+ else target = 1
+ return origShow.call(this).fadeTo(speed, target, callback)
+ }
+
+ $.fn.fadeOut = function(speed, callback) {
+ return hide(this, speed, null, callback)
+ }
+
+ $.fn.fadeToggle = function(speed, callback) {
+ return this.each(function(){
+ var el = $(this)
+ el[
+ (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut'
+ ](speed, callback)
+ })
+ }
+
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ if ($.os.ios) {
+ var gesture = {}, gestureTimeout
+
+ function parentIfText(node){
+ return 'tagName' in node ? node : node.parentNode
+ }
+
+ $(document).bind('gesturestart', function(e){
+ var now = Date.now(), delta = now - (gesture.last || now)
+ gesture.target = parentIfText(e.target)
+ gestureTimeout && clearTimeout(gestureTimeout)
+ gesture.e1 = e.scale
+ gesture.last = now
+ }).bind('gesturechange', function(e){
+ gesture.e2 = e.scale
+ }).bind('gestureend', function(e){
+ if (gesture.e2 > 0) {
+ Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') &&
+ $(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out'))
+ gesture.e1 = gesture.e2 = gesture.last = 0
+ } else if ('last' in gesture) {
+ gesture = {}
+ }
+ })
+
+ ;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){
+ $.fn[m] = function(callback){ return this.bind(m, callback) }
+ })
+ }
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function(undefined){
+ if (String.prototype.trim === undefined) // fix for iOS 3.2
+ String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') }
+
+ // For iOS 3.x
+ // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
+ if (Array.prototype.reduce === undefined)
+ Array.prototype.reduce = function(fun){
+ if(this === void 0 || this === null) throw new TypeError()
+ var t = Object(this), len = t.length >>> 0, k = 0, accumulator
+ if(typeof fun != 'function') throw new TypeError()
+ if(len == 0 && arguments.length == 1) throw new TypeError()
+
+ if(arguments.length >= 2)
+ accumulator = arguments[1]
+ else
+ do{
+ if(k in t){
+ accumulator = t[k++]
+ break
+ }
+ if(++k >= len) throw new TypeError()
+ } while (true)
+
+ while (k < len){
+ if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
+ k++
+ }
+ return accumulator
+ }
+
+})()
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 @@
+;(function($){
+ var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches
+
+ function visible(elem){
+ elem = $(elem)
+ return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
+ }
+
+ // Implements a subset from:
+ // http://api.jquery.com/category/selectors/jquery-selector-extensions/
+ //
+ // Each filter function receives the current index, all nodes in the
+ // considered set, and a value if there were parentheses. The value
+ // of `this` is the node currently being considered. The function returns the
+ // resulting node(s), null, or undefined.
+ //
+ // Complex selectors are not supported:
+ // li:has(label:contains("foo")) + li:has(label:contains("bar"))
+ // ul.inner:first > li
+ var filters = $.expr[':'] = {
+ visible: function(){ if (visible(this)) return this },
+ hidden: function(){ if (!visible(this)) return this },
+ selected: function(){ if (this.selected) return this },
+ checked: function(){ if (this.checked) return this },
+ parent: function(){ return this.parentNode },
+ first: function(idx){ if (idx === 0) return this },
+ last: function(idx, nodes){ if (idx === nodes.length - 1) return this },
+ eq: function(idx, _, value){ if (idx === value) return this },
+ contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this },
+ has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this }
+ }
+
+ var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'),
+ childRe = /^\s*>/,
+ classTag = 'Zepto' + (+new Date())
+
+ function process(sel, fn) {
+ // quote the hash in `a[href^=#]` expression
+ sel = sel.replace(/=#\]/g, '="#"]')
+ var filter, arg, match = filterRe.exec(sel)
+ if (match && match[2] in filters) {
+ var filter = filters[match[2]], arg = match[3]
+ sel = match[1]
+ if (arg) {
+ var num = Number(arg)
+ if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
+ else arg = num
+ }
+ }
+ return fn(sel, filter, arg)
+ }
+
+ zepto.qsa = function(node, selector) {
+ return process(selector, function(sel, filter, arg){
+ try {
+ var taggedParent
+ if (!sel && filter) sel = '*'
+ else if (childRe.test(sel))
+ // support "> *" child queries by tagging the parent node with a
+ // unique class and prepending that classname onto the selector
+ taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel
+
+ var nodes = oldQsa(node, sel)
+ } catch(e) {
+ console.error('error performing selector: %o', selector)
+ throw e
+ } finally {
+ if (taggedParent) taggedParent.removeClass(classTag)
+ }
+ return !filter ? nodes :
+ zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
+ })
+ }
+
+ zepto.matches = function(node, selector){
+ return process(selector, function(sel, filter, arg){
+ return (!sel || oldMatches(node, sel)) &&
+ (!filter || filter.call(node, null, arg) === node)
+ })
+ }
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ $.fn.end = function(){
+ return this.prevObject || $()
+ }
+
+ $.fn.andSelf = function(){
+ return this.add(this.prevObject || $())
+ }
+
+ 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
+ var fn = $.fn[property]
+ $.fn[property] = function(){
+ var ret = fn.apply(this, arguments)
+ ret.prevObject = this
+ return ret
+ }
+ })
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ var touch = {},
+ touchTimeout, tapTimeout, swipeTimeout,
+ longTapDelay = 750, longTapTimeout
+
+ function parentIfText(node) {
+ return 'tagName' in node ? node : node.parentNode
+ }
+
+ function swipeDirection(x1, x2, y1, y2) {
+ var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2)
+ return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
+ }
+
+ function longTap() {
+ longTapTimeout = null
+ if (touch.last) {
+ touch.el.trigger('longTap')
+ touch = {}
+ }
+ }
+
+ function cancelLongTap() {
+ if (longTapTimeout) clearTimeout(longTapTimeout)
+ longTapTimeout = null
+ }
+
+ function cancelAll() {
+ if (touchTimeout) clearTimeout(touchTimeout)
+ if (tapTimeout) clearTimeout(tapTimeout)
+ if (swipeTimeout) clearTimeout(swipeTimeout)
+ if (longTapTimeout) clearTimeout(longTapTimeout)
+ touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
+ touch = {}
+ }
+
+ $(document).ready(function(){
+ var now, delta
+
+ $(document.body)
+ .bind('touchstart', function(e){
+ now = Date.now()
+ delta = now - (touch.last || now)
+ touch.el = $(parentIfText(e.touches[0].target))
+ touchTimeout && clearTimeout(touchTimeout)
+ touch.x1 = e.touches[0].pageX
+ touch.y1 = e.touches[0].pageY
+ if (delta > 0 && delta <= 250) touch.isDoubleTap = true
+ touch.last = now
+ longTapTimeout = setTimeout(longTap, longTapDelay)
+ })
+ .bind('touchmove', function(e){
+ cancelLongTap()
+ touch.x2 = e.touches[0].pageX
+ touch.y2 = e.touches[0].pageY
+ })
+ .bind('touchend', function(e){
+ cancelLongTap()
+
+ // swipe
+ if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
+ (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
+
+ swipeTimeout = setTimeout(function() {
+ touch.el.trigger('swipe')
+ touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
+ touch = {}
+ }, 0)
+
+ // normal tap
+ else if ('last' in touch)
+
+ // delay by one tick so we can cancel the 'tap' event if 'scroll' fires
+ // ('tap' fires before 'scroll')
+ tapTimeout = setTimeout(function() {
+
+ // trigger universal 'tap' with the option to cancelTouch()
+ // (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
+ var event = $.Event('tap')
+ event.cancelTouch = cancelAll
+ touch.el.trigger(event)
+
+ // trigger double tap immediately
+ if (touch.isDoubleTap) {
+ touch.el.trigger('doubleTap')
+ touch = {}
+ }
+
+ // trigger single tap after 250ms of inactivity
+ else {
+ touchTimeout = setTimeout(function(){
+ touchTimeout = null
+ touch.el.trigger('singleTap')
+ touch = {}
+ }, 250)
+ }
+
+ }, 0)
+
+ })
+ .bind('touchcancel', cancelAll)
+
+ $(window).bind('scroll', cancelAll)
+ })
+
+ ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
+ $.fn[m] = function(callback){ return this.bind(m, callback) }
+ })
+})(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 @@
+// Zepto.js
+// (c) 2010-2012 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+var Zepto = (function() {
+ var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
+ document = window.document,
+ elementDisplay = {}, classCache = {},
+ getComputedStyle = document.defaultView.getComputedStyle,
+ cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
+ fragmentRE = /^\s*<(\w+|!)[^>]*>/,
+ tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rootNodeRE = /^(?:body|html)$/i,
+
+ // special attributes that should be get/set via method calls
+ methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
+
+ adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
+ table = document.createElement('table'),
+ tableRow = document.createElement('tr'),
+ containers = {
+ 'tr': document.createElement('tbody'),
+ 'tbody': table, 'thead': table, 'tfoot': table,
+ 'td': tableRow, 'th': tableRow,
+ '*': document.createElement('div')
+ },
+ readyRE = /complete|loaded|interactive/,
+ classSelectorRE = /^\.([\w-]+)$/,
+ idSelectorRE = /^#([\w-]*)$/,
+ tagSelectorRE = /^[\w-]+$/,
+ toString = {}.toString,
+ zepto = {},
+ camelize, uniq,
+ tempParent = document.createElement('div')
+
+ zepto.matches = function(element, selector) {
+ if (!element || element.nodeType !== 1) return false
+ var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
+ element.oMatchesSelector || element.matchesSelector
+ if (matchesSelector) return matchesSelector.call(element, selector)
+ // fall back to performing a selector:
+ var match, parent = element.parentNode, temp = !parent
+ if (temp) (parent = tempParent).appendChild(element)
+ match = ~zepto.qsa(parent, selector).indexOf(element)
+ temp && tempParent.removeChild(element)
+ return match
+ }
+
+ function isFunction(value) { return toString.call(value) == "[object Function]" }
+ function isObject(value) { return value instanceof Object }
+ function isPlainObject(value) {
+ return isObject(value) && value != window && value.__proto__ == Object.prototype
+ }
+ function isArray(value) { return value instanceof Array }
+ function likeArray(obj) { return typeof obj.length == 'number' }
+
+ function compact(array) { return filter.call(array, function(item){ return item != null }) }
+ function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
+ camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
+ function dasherize(str) {
+ return str.replace(/::/g, '/')
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+ .replace(/_/g, '-')
+ .toLowerCase()
+ }
+ uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
+
+ function classRE(name) {
+ return name in classCache ?
+ classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
+ }
+
+ function maybeAddPx(name, value) {
+ return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
+ }
+
+ function defaultDisplay(nodeName) {
+ var element, display
+ if (!elementDisplay[nodeName]) {
+ element = document.createElement(nodeName)
+ document.body.appendChild(element)
+ display = getComputedStyle(element, '').getPropertyValue("display")
+ element.parentNode.removeChild(element)
+ display == "none" && (display = "block")
+ elementDisplay[nodeName] = display
+ }
+ return elementDisplay[nodeName]
+ }
+
+ function children(element) {
+ return 'children' in element ?
+ slice.call(element.children) :
+ $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
+ }
+
+ // `$.zepto.fragment` takes a html string and an optional tag name
+ // to generate DOM nodes nodes from the given html string.
+ // The generated DOM nodes are returned as an array.
+ // This function can be overriden in plugins for example to make
+ // it compatible with browsers that don't support the DOM fully.
+ zepto.fragment = function(html, name, properties) {
+ if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
+ if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
+ if (!(name in containers)) name = '*'
+
+ var nodes, dom, container = containers[name]
+ container.innerHTML = '' + html
+ dom = $.each(slice.call(container.childNodes), function(){
+ container.removeChild(this)
+ })
+ if (isPlainObject(properties)) {
+ nodes = $(dom)
+ $.each(properties, function(key, value) {
+ if (methodAttributes.indexOf(key) > -1) nodes[key](value)
+ else nodes.attr(key, value)
+ })
+ }
+ return dom
+ }
+
+ // `$.zepto.Z` swaps out the prototype of the given `dom` array
+ // of nodes with `$.fn` and thus supplying all the Zepto functions
+ // to the array. Note that `__proto__` is not supported on Internet
+ // Explorer. This method can be overriden in plugins.
+ zepto.Z = function(dom, selector) {
+ dom = dom || []
+ dom.__proto__ = arguments.callee.prototype
+ dom.selector = selector || ''
+ return dom
+ }
+
+ // `$.zepto.isZ` should return `true` if the given object is a Zepto
+ // collection. This method can be overriden in plugins.
+ zepto.isZ = function(object) {
+ return object instanceof zepto.Z
+ }
+
+ // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
+ // takes a CSS selector and an optional context (and handles various
+ // special cases).
+ // This method can be overriden in plugins.
+ zepto.init = function(selector, context) {
+ // If nothing given, return an empty Zepto collection
+ if (!selector) return zepto.Z()
+ // If a function is given, call it when the DOM is ready
+ else if (isFunction(selector)) return $(document).ready(selector)
+ // If a Zepto collection is given, juts return it
+ else if (zepto.isZ(selector)) return selector
+ else {
+ var dom
+ // normalize array if an array of nodes is given
+ if (isArray(selector)) dom = compact(selector)
+ // Wrap DOM nodes. If a plain object is given, duplicate it.
+ else if (isObject(selector))
+ dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
+ // If it's a html fragment, create nodes from it
+ else if (fragmentRE.test(selector))
+ dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
+ // If there's a context, create a collection on that context first, and select
+ // nodes from there
+ else if (context !== undefined) return $(context).find(selector)
+ // And last but no least, if it's a CSS selector, use it to select nodes.
+ else dom = zepto.qsa(document, selector)
+ // create a new Zepto collection from the nodes found
+ return zepto.Z(dom, selector)
+ }
+ }
+
+ // `$` will be the base `Zepto` object. When calling this
+ // function just call `$.zepto.init, which makes the implementation
+ // details of selecting nodes and creating Zepto collections
+ // patchable in plugins.
+ $ = function(selector, context){
+ return zepto.init(selector, context)
+ }
+
+ function extend(target, source, deep) {
+ for (key in source)
+ if (deep && isPlainObject(source[key])) {
+ if (!isPlainObject(target[key])) target[key] = {}
+ extend(target[key], source[key], deep)
+ }
+ else if (source[key] !== undefined) target[key] = source[key]
+ }
+
+ // Copy all but undefined properties from one or more
+ // objects to the `target` object.
+ $.extend = function(target){
+ var deep, args = slice.call(arguments, 1)
+ if (typeof target == 'boolean') {
+ deep = target
+ target = args.shift()
+ }
+ args.forEach(function(arg){ extend(target, arg, deep) })
+ return target
+ }
+
+ // `$.zepto.qsa` is Zepto's CSS selector implementation which
+ // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
+ // This method can be overriden in plugins.
+ zepto.qsa = function(element, selector){
+ var found
+ return (element === document && idSelectorRE.test(selector)) ?
+ ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) :
+ (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
+ slice.call(
+ classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
+ tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
+ element.querySelectorAll(selector)
+ )
+ }
+
+ function filtered(nodes, selector) {
+ return selector === undefined ? $(nodes) : $(nodes).filter(selector)
+ }
+
+ $.contains = function(parent, node) {
+ return parent !== node && parent.contains(node)
+ }
+
+ function funcArg(context, arg, idx, payload) {
+ return isFunction(arg) ? arg.call(context, idx, payload) : arg
+ }
+
+ function setAttribute(node, name, value) {
+ value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
+ }
+
+ // access className property while respecting SVGAnimatedString
+ function className(node, value){
+ var klass = node.className,
+ svg = klass && klass.baseVal !== undefined
+
+ if (value === undefined) return svg ? klass.baseVal : klass
+ svg ? (klass.baseVal = value) : (node.className = value)
+ }
+
+ // "true" => true
+ // "false" => false
+ // "null" => null
+ // "42" => 42
+ // "42.5" => 42.5
+ // JSON => parse if valid
+ // String => self
+ function deserializeValue(value) {
+ var num
+ try {
+ return value ?
+ value == "true" ||
+ ( value == "false" ? false :
+ value == "null" ? null :
+ !isNaN(num = Number(value)) ? num :
+ /^[\[\{]/.test(value) ? $.parseJSON(value) :
+ value )
+ : value
+ } catch(e) {
+ return value
+ }
+ }
+
+ $.isFunction = isFunction
+ $.isObject = isObject
+ $.isArray = isArray
+ $.isPlainObject = isPlainObject
+
+ $.inArray = function(elem, array, i){
+ return emptyArray.indexOf.call(array, elem, i)
+ }
+
+ $.camelCase = camelize
+ $.trim = function(str) { return str.trim() }
+
+ // plugin compatibility
+ $.uuid = 0
+ $.support = { }
+ $.expr = { }
+
+ $.map = function(elements, callback){
+ var value, values = [], i, key
+ if (likeArray(elements))
+ for (i = 0; i < elements.length; i++) {
+ value = callback(elements[i], i)
+ if (value != null) values.push(value)
+ }
+ else
+ for (key in elements) {
+ value = callback(elements[key], key)
+ if (value != null) values.push(value)
+ }
+ return flatten(values)
+ }
+
+ $.each = function(elements, callback){
+ var i, key
+ if (likeArray(elements)) {
+ for (i = 0; i < elements.length; i++)
+ if (callback.call(elements[i], i, elements[i]) === false) return elements
+ } else {
+ for (key in elements)
+ if (callback.call(elements[key], key, elements[key]) === false) return elements
+ }
+
+ return elements
+ }
+
+ $.grep = function(elements, callback){
+ return filter.call(elements, callback)
+ }
+
+ if (window.JSON) $.parseJSON = JSON.parse
+
+ // Define methods that will be available on all
+ // Zepto collections
+ $.fn = {
+ // Because a collection acts like an array
+ // copy over these useful array functions.
+ forEach: emptyArray.forEach,
+ reduce: emptyArray.reduce,
+ push: emptyArray.push,
+ sort: emptyArray.sort,
+ indexOf: emptyArray.indexOf,
+ concat: emptyArray.concat,
+
+ // `map` and `slice` in the jQuery API work differently
+ // from their array counterparts
+ map: function(fn){
+ return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
+ },
+ slice: function(){
+ return $(slice.apply(this, arguments))
+ },
+
+ ready: function(callback){
+ if (readyRE.test(document.readyState)) callback($)
+ else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
+ return this
+ },
+ get: function(idx){
+ return idx === undefined ? slice.call(this) : this[idx]
+ },
+ toArray: function(){ return this.get() },
+ size: function(){
+ return this.length
+ },
+ remove: function(){
+ return this.each(function(){
+ if (this.parentNode != null)
+ this.parentNode.removeChild(this)
+ })
+ },
+ each: function(callback){
+ emptyArray.every.call(this, function(el, idx){
+ return callback.call(el, idx, el) !== false
+ })
+ return this
+ },
+ filter: function(selector){
+ if (isFunction(selector)) return this.not(this.not(selector))
+ return $(filter.call(this, function(element){
+ return zepto.matches(element, selector)
+ }))
+ },
+ add: function(selector,context){
+ return $(uniq(this.concat($(selector,context))))
+ },
+ is: function(selector){
+ return this.length > 0 && zepto.matches(this[0], selector)
+ },
+ not: function(selector){
+ var nodes=[]
+ if (isFunction(selector) && selector.call !== undefined)
+ this.each(function(idx){
+ if (!selector.call(this,idx)) nodes.push(this)
+ })
+ else {
+ var excludes = typeof selector == 'string' ? this.filter(selector) :
+ (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
+ this.forEach(function(el){
+ if (excludes.indexOf(el) < 0) nodes.push(el)
+ })
+ }
+ return $(nodes)
+ },
+ has: function(selector){
+ return this.filter(function(){
+ return isObject(selector) ?
+ $.contains(this, selector) :
+ $(this).find(selector).size()
+ })
+ },
+ eq: function(idx){
+ return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
+ },
+ first: function(){
+ var el = this[0]
+ return el && !isObject(el) ? el : $(el)
+ },
+ last: function(){
+ var el = this[this.length - 1]
+ return el && !isObject(el) ? el : $(el)
+ },
+ find: function(selector){
+ var result
+ if (this.length == 1) result = $(zepto.qsa(this[0], selector))
+ else result = this.map(function(){ return zepto.qsa(this, selector) })
+ return result
+ },
+ closest: function(selector, context){
+ var node = this[0]
+ while (node && !zepto.matches(node, selector))
+ node = node !== context && node !== document && node.parentNode
+ return $(node)
+ },
+ parents: function(selector){
+ var ancestors = [], nodes = this
+ while (nodes.length > 0)
+ nodes = $.map(nodes, function(node){
+ if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
+ ancestors.push(node)
+ return node
+ }
+ })
+ return filtered(ancestors, selector)
+ },
+ parent: function(selector){
+ return filtered(uniq(this.pluck('parentNode')), selector)
+ },
+ children: function(selector){
+ return filtered(this.map(function(){ return children(this) }), selector)
+ },
+ contents: function() {
+ return this.map(function() { return slice.call(this.childNodes) })
+ },
+ siblings: function(selector){
+ return filtered(this.map(function(i, el){
+ return filter.call(children(el.parentNode), function(child){ return child!==el })
+ }), selector)
+ },
+ empty: function(){
+ return this.each(function(){ this.innerHTML = '' })
+ },
+ // `pluck` is borrowed from Prototype.js
+ pluck: function(property){
+ return $.map(this, function(el){ return el[property] })
+ },
+ show: function(){
+ return this.each(function(){
+ this.style.display == "none" && (this.style.display = null)
+ if (getComputedStyle(this, '').getPropertyValue("display") == "none")
+ this.style.display = defaultDisplay(this.nodeName)
+ })
+ },
+ replaceWith: function(newContent){
+ return this.before(newContent).remove()
+ },
+ wrap: function(structure){
+ var func = isFunction(structure)
+ if (this[0] && !func)
+ var dom = $(structure).get(0),
+ clone = dom.parentNode || this.length > 1
+
+ return this.each(function(index){
+ $(this).wrapAll(
+ func ? structure.call(this, index) :
+ clone ? dom.cloneNode(true) : dom
+ )
+ })
+ },
+ wrapAll: function(structure){
+ if (this[0]) {
+ $(this[0]).before(structure = $(structure))
+ var children
+ // drill down to the inmost element
+ while ((children = structure.children()).length) structure = children.first()
+ $(structure).append(this)
+ }
+ return this
+ },
+ wrapInner: function(structure){
+ var func = isFunction(structure)
+ return this.each(function(index){
+ var self = $(this), contents = self.contents(),
+ dom = func ? structure.call(this, index) : structure
+ contents.length ? contents.wrapAll(dom) : self.append(dom)
+ })
+ },
+ unwrap: function(){
+ this.parent().each(function(){
+ $(this).replaceWith($(this).children())
+ })
+ return this
+ },
+ clone: function(){
+ return this.map(function(){ return this.cloneNode(true) })
+ },
+ hide: function(){
+ return this.css("display", "none")
+ },
+ toggle: function(setting){
+ return this.each(function(){
+ var el = $(this)
+ ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
+ })
+ },
+ prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
+ next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
+ html: function(html){
+ return html === undefined ?
+ (this.length > 0 ? this[0].innerHTML : null) :
+ this.each(function(idx){
+ var originHtml = this.innerHTML
+ $(this).empty().append( funcArg(this, html, idx, originHtml) )
+ })
+ },
+ text: function(text){
+ return text === undefined ?
+ (this.length > 0 ? this[0].textContent : null) :
+ this.each(function(){ this.textContent = text })
+ },
+ attr: function(name, value){
+ var result
+ return (typeof name == 'string' && value === undefined) ?
+ (this.length == 0 || this[0].nodeType !== 1 ? undefined :
+ (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
+ (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
+ ) :
+ this.each(function(idx){
+ if (this.nodeType !== 1) return
+ if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
+ else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
+ })
+ },
+ removeAttr: function(name){
+ return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) })
+ },
+ prop: function(name, value){
+ return (value === undefined) ?
+ (this[0] && this[0][name]) :
+ this.each(function(idx){
+ this[name] = funcArg(this, value, idx, this[name])
+ })
+ },
+ data: function(name, value){
+ var data = this.attr('data-' + dasherize(name), value)
+ return data !== null ? deserializeValue(data) : undefined
+ },
+ val: function(value){
+ return (value === undefined) ?
+ (this[0] && (this[0].multiple ?
+ $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') :
+ this[0].value)
+ ) :
+ this.each(function(idx){
+ this.value = funcArg(this, value, idx, this.value)
+ })
+ },
+ offset: function(coordinates){
+ if (coordinates) return this.each(function(index){
+ var $this = $(this),
+ coords = funcArg(this, coordinates, index, $this.offset()),
+ parentOffset = $this.offsetParent().offset(),
+ props = {
+ top: coords.top - parentOffset.top,
+ left: coords.left - parentOffset.left
+ }
+
+ if ($this.css('position') == 'static') props['position'] = 'relative'
+ $this.css(props)
+ })
+ if (this.length==0) return null
+ var obj = this[0].getBoundingClientRect()
+ return {
+ left: obj.left + window.pageXOffset,
+ top: obj.top + window.pageYOffset,
+ width: obj.width,
+ height: obj.height
+ }
+ },
+ css: function(property, value){
+ if (arguments.length < 2 && typeof property == 'string')
+ return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property))
+
+ var css = ''
+ for (key in property)
+ if (!property[key] && property[key] !== 0)
+ this.each(function(){ this.style.removeProperty(dasherize(key)) })
+ else
+ css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
+
+ if (typeof property == 'string')
+ if (!value && value !== 0)
+ this.each(function(){ this.style.removeProperty(dasherize(property)) })
+ else
+ css = dasherize(property) + ":" + maybeAddPx(property, value)
+
+ return this.each(function(){ this.style.cssText += ';' + css })
+ },
+ index: function(element){
+ return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
+ },
+ hasClass: function(name){
+ return emptyArray.some.call(this, function(el){
+ return this.test(className(el))
+ }, classRE(name))
+ },
+ addClass: function(name){
+ return this.each(function(idx){
+ classList = []
+ var cls = className(this), newName = funcArg(this, name, idx, cls)
+ newName.split(/\s+/g).forEach(function(klass){
+ if (!$(this).hasClass(klass)) classList.push(klass)
+ }, this)
+ classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
+ })
+ },
+ removeClass: function(name){
+ return this.each(function(idx){
+ if (name === undefined) return className(this, '')
+ classList = className(this)
+ funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
+ classList = classList.replace(classRE(klass), " ")
+ })
+ className(this, classList.trim())
+ })
+ },
+ toggleClass: function(name, when){
+ return this.each(function(idx){
+ var newName = funcArg(this, name, idx, className(this))
+ ;(when === undefined ? !$(this).hasClass(newName) : when) ?
+ $(this).addClass(newName) : $(this).removeClass(newName)
+ })
+ },
+ scrollTop: function(){
+ if (!this.length) return
+ return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY
+ },
+ position: function() {
+ if (!this.length) return
+
+ var elem = this[0],
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
+ offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
+ parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ }
+ },
+ offsetParent: function() {
+ return this.map(function(){
+ var parent = this.offsetParent || document.body
+ while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
+ parent = parent.offsetParent
+ return parent
+ })
+ }
+ }
+
+ // for now
+ $.fn.detach = $.fn.remove
+
+ // Generate the `width` and `height` functions
+ ;['width', 'height'].forEach(function(dimension){
+ $.fn[dimension] = function(value){
+ var offset, Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() })
+ if (value === undefined) return this[0] == window ? window['inner' + Dimension] :
+ this[0] == document ? document.documentElement['offset' + Dimension] :
+ (offset = this.offset()) && offset[dimension]
+ else return this.each(function(idx){
+ var el = $(this)
+ el.css(dimension, funcArg(this, value, idx, el[dimension]()))
+ })
+ }
+ })
+
+ function traverseNode(node, fun) {
+ fun(node)
+ for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
+ }
+
+ // Generate the `after`, `prepend`, `before`, `append`,
+ // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
+ adjacencyOperators.forEach(function(operator, operatorIndex) {
+ var inside = operatorIndex % 2 //=> prepend, append
+
+ $.fn[operator] = function(){
+ // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
+ var nodes = $.map(arguments, function(n){ return isObject(n) ? n : zepto.fragment(n) }),
+ parent, copyByClone = this.length > 1
+ if (nodes.length < 1) return this
+
+ return this.each(function(_, target){
+ parent = inside ? target : target.parentNode
+
+ // convert all methods to a "before" operation
+ target = operatorIndex == 0 ? target.nextSibling :
+ operatorIndex == 1 ? target.firstChild :
+ operatorIndex == 2 ? target :
+ null
+
+ nodes.forEach(function(node){
+ if (copyByClone) node = node.cloneNode(true)
+ else if (!parent) return $(node).remove()
+
+ traverseNode(parent.insertBefore(node, target), function(el){
+ if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
+ (!el.type || el.type === 'text/javascript') && !el.src)
+ window['eval'].call(window, el.innerHTML)
+ })
+ })
+ })
+ }
+
+ // after => insertAfter
+ // prepend => prependTo
+ // before => insertBefore
+ // append => appendTo
+ $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
+ $(html)[operator](this)
+ return this
+ }
+ })
+
+ zepto.Z.prototype = $.fn
+
+ // Export internal API functions in the `$.zepto` namespace
+ zepto.uniq = uniq
+ zepto.deserializeValue = deserializeValue
+ $.zepto = zepto
+
+ return $
+})()
+
+// If `$` is not yet defined, point it to `Zepto`
+window.Zepto = Zepto
+'$' 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
@@ -26,32 +26,39 @@ refer to http://www.clipperz.com.
function _pm_logEvent(anEvent) {
// console.log("####", anEvent);
anEvent.preventDefault();
}
function handleGenericDeferredError(anError) {
var result;
if (anError instanceof MochiKit.Async.CancelledError) {
result = anError;
} else {
MochiKit.Logging.logError("## MainController - GENERIC ERROR" + "\n" + "==>> " + anError + " <<==\n" + anError.stack);
result = new MochiKit.Async.CancelledError(anError);
}
return result;
}
Clipperz.PM.RunTime = {};
function run() {
- MochiKit.DOM.removeElement('javaScriptAlert');
Clipperz.PM.Strings.Languages.initSetup();
- Clipperz.PM.RunTime.mainController = new Clipperz.PM.UI.iPhone.Controllers.MainController();
- Clipperz.PM.RunTime.mainController.run(false);
+ Clipperz.PM.RunTime.mainController = new Clipperz.PM.UI.Mobile.Controllers.MainController();
+ Clipperz.PM.RunTime.mainController.run();
}
+// if (navigator.standalone == false) {
+// window.localStorage.setItem('PIN', '1234');
+// alert("Saved PIN");
+// } else {
+// alert (window.localStorage.getItem('PIN'));
+// }
+
+
MochiKit.DOM.addLoadEvent(run);