1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-22 08:52:15 -05:00

refactored all files to use functional strict scope

This commit is contained in:
Tankred Hase 2013-04-02 00:12:15 +02:00
parent 5ee864fe01
commit 5c0e04cc31
26 changed files with 1669 additions and 1505 deletions

View File

@ -1,4 +1,4 @@
var app;
var app; // container for the application namespace
(function() {
'use strict';

View File

@ -1,17 +1,16 @@
'use strict';
(function() {
'use strict';
// import web worker dependencies
importScripts('../../lib/sjcl/sjcl.js');
importScripts('../../lib/sjcl/bitArray.js');
importScripts('../../lib/sjcl/codecBase64.js');
importScripts('../../lib/sjcl/codecString.js');
importScripts('../../lib/sjcl/aes.js');
importScripts('../../lib/sjcl/ccm.js');
importScripts('../app-config.js');
importScripts('./aes-ccm.js');
importScripts('./util.js');
var AESBATCHWORKER = (function () {
// import web worker dependencies
importScripts('../../lib/sjcl/sjcl.js');
importScripts('../../lib/sjcl/bitArray.js');
importScripts('../../lib/sjcl/codecBase64.js');
importScripts('../../lib/sjcl/codecString.js');
importScripts('../../lib/sjcl/aes.js');
importScripts('../../lib/sjcl/ccm.js');
importScripts('../app-config.js');
importScripts('./aes-ccm.js');
importScripts('./util.js');
/**
* In the web worker thread context, 'this' and 'self' can be used as a global

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* A Wrapper for Crypto.js's AES-CBC encryption
*/
app.crypto.AesCBC = function() {
app.crypto.AesCBC = function() {
var mode = CryptoJS.mode.CBC; // use CBC mode for Crypto.js
var padding = CryptoJS.pad.Pkcs7; // use Pkcs7/Pkcs5 padding for Crypto.js
@ -21,7 +22,11 @@ app.crypto.AesCBC = function() {
var ivWords = CryptoJS.enc.Base64.parse(iv);
var plaintextWords = CryptoJS.enc.Utf8.parse(plaintext);
var encrypted = CryptoJS.AES.encrypt(plaintextWords, keyWords, { iv: ivWords, mode: mode, padding: padding });
var encrypted = CryptoJS.AES.encrypt(plaintextWords, keyWords, {
iv: ivWords,
mode: mode,
padding: padding
});
var ctBase64 = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
return ctBase64;
@ -39,10 +44,16 @@ app.crypto.AesCBC = function() {
var keyWords = CryptoJS.enc.Base64.parse(key);
var ivWords = CryptoJS.enc.Base64.parse(iv);
var decrypted = CryptoJS.AES.decrypt(ciphertext, keyWords, { iv: ivWords, mode: mode, padding: padding });
var decrypted = CryptoJS.AES.decrypt(ciphertext, keyWords, {
iv: ivWords,
mode: mode,
padding: padding
});
var pt = decrypted.toString(CryptoJS.enc.Utf8);
return pt;
};
};
};
}());

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* A Wrapper for SJCL's authenticated AES-CCM encryption
*/
app.crypto.AesCCM = function() {
app.crypto.AesCCM = function() {
var adata = []; // authenticated data (empty by default)
var tlen = 64; // The tag length in bits
@ -48,4 +49,6 @@ app.crypto.AesCCM = function() {
return pt;
};
};
};
}());

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* A Wrapper for SJCL's authenticated AES-GCM encryption
*/
app.crypto.AesGCM = function() {
app.crypto.AesGCM = function() {
var adata = []; // authenticated data (empty by default)
var tlen = 128; // The tag length in bits
@ -48,4 +49,6 @@ app.crypto.AesGCM = function() {
return pt;
};
};
};
}());

View File

@ -1,16 +1,15 @@
'use strict';
(function() {
'use strict';
// import web worker dependencies
importScripts('../../lib/sjcl/sjcl.js');
importScripts('../../lib/sjcl/bitArray.js');
importScripts('../../lib/sjcl/codecBase64.js');
importScripts('../../lib/sjcl/codecString.js');
importScripts('../../lib/sjcl/aes.js');
importScripts('../../lib/sjcl/ccm.js');
importScripts('../app-config.js');
importScripts('./aes-ccm.js');
var AESWORKER = (function () {
// import web worker dependencies
importScripts('../../lib/sjcl/sjcl.js');
importScripts('../../lib/sjcl/bitArray.js');
importScripts('../../lib/sjcl/codecBase64.js');
importScripts('../../lib/sjcl/codecString.js');
importScripts('../../lib/sjcl/aes.js');
importScripts('../../lib/sjcl/ccm.js');
importScripts('../app-config.js');
importScripts('./aes-ccm.js');
/**
* In the web worker thread context, 'this' and 'self' can be used as a global

View File

@ -1,10 +1,11 @@
'use strict';
(function() {
'use strict';
/**
/**
* High level crypto api that invokes native crypto (if available) and
* gracefully degrades to JS crypto (if unavailable)
*/
app.crypto.Crypto = function(window, util) {
app.crypto.Crypto = function(window, util) {
var symmetricUserKey, // the user's secret key used to encrypt item-keys
aes = new app.crypto.AesCCM(); // use authenticated AES-CCM mode by default
@ -27,12 +28,15 @@ app.crypto.Crypto = function(window, util) {
var encryptedKey = keyStore.read(storageId);
// check if key exists
if(!encryptedKey) {
if (!encryptedKey) {
// generate key, encrypt and persist if none exists
symmetricUserKey = util.random(keySize);
var iv = util.random(ivSize);
var key = aes.encrypt(symmetricUserKey, pbkdf2, iv);
keyStore.persist(storageId, { key: key, iv: iv });
keyStore.persist(storageId, {
key: key,
iv: iv
});
} else {
// decrypt key
symmetricUserKey = aes.decrypt(encryptedKey.key, pbkdf2, encryptedKey.iv);
@ -58,7 +62,10 @@ app.crypto.Crypto = function(window, util) {
}, false);
// send plaintext data to the worker
worker.postMessage({ password:password, keySize:keySize });
worker.postMessage({
password: password,
keySize: keySize
});
} else {
// no WebWorker support... do synchronous call
@ -79,7 +86,12 @@ app.crypto.Crypto = function(window, util) {
worker.addEventListener('message', function(e) {
callback(e.data);
}, false);
worker.postMessage({ type:'encrypt', plaintext:plaintext, key:key, iv:iv });
worker.postMessage({
type: 'encrypt',
plaintext: plaintext,
key: key,
iv: iv
});
} else {
var ct = this.aesEncryptSync(plaintext, key, iv);
@ -94,7 +106,12 @@ app.crypto.Crypto = function(window, util) {
worker.addEventListener('message', function(e) {
callback(e.data);
}, false);
worker.postMessage({ type:'decrypt', ciphertext:ciphertext, key:key, iv:iv });
worker.postMessage({
type: 'decrypt',
ciphertext: ciphertext,
key: key,
iv: iv
});
} else {
var pt = this.aesDecryptSync(ciphertext, key, iv);
@ -121,7 +138,10 @@ app.crypto.Crypto = function(window, util) {
worker.addEventListener('message', function(e) {
callback(e.data);
}, false);
worker.postMessage({ type:'encrypt', list:list });
worker.postMessage({
type: 'encrypt',
list: list
});
} else {
var encryptedList = util.encryptList(aes, list);
@ -136,7 +156,10 @@ app.crypto.Crypto = function(window, util) {
worker.addEventListener('message', function(e) {
callback(e.data);
}, false);
worker.postMessage({ type:'decrypt', list:list });
worker.postMessage({
type: 'decrypt',
list: list
});
} else {
var decryptedList = util.decryptList(aes, list);
@ -164,7 +187,8 @@ app.crypto.Crypto = function(window, util) {
};
this.aesEncryptListForUser = function(list, callback) {
var i, envelope, envelopes = [], self = this;
var i, envelope, envelopes = [],
self = this;
// package objects into batchable envelope format
for (i = 0; i < list.length; i++) {
@ -220,4 +244,6 @@ app.crypto.Crypto = function(window, util) {
});
};
};
};
}());

View File

@ -1,15 +1,14 @@
'use strict';
(function() {
'use strict';
// import web worker dependencies
importScripts('../../lib/crypto-js/core.js');
importScripts('../../lib/crypto-js/enc-base64.js');
importScripts('../../lib/crypto-js/sha1.js');
importScripts('../../lib/crypto-js/hmac.js');
importScripts('../../lib/crypto-js/pbkdf2.js');
importScripts('../app-config.js');
importScripts('./pbkdf2.js');
var PBKDF2WORKER = (function () {
// import web worker dependencies
importScripts('../../lib/crypto-js/core.js');
importScripts('../../lib/crypto-js/enc-base64.js');
importScripts('../../lib/crypto-js/sha1.js');
importScripts('../../lib/crypto-js/hmac.js');
importScripts('../../lib/crypto-js/pbkdf2.js');
importScripts('../app-config.js');
importScripts('./pbkdf2.js');
/**
* In the web worker thread context, 'this' and 'self' can be used as a global

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* A Wrapper for Crypto.js's PBKDF2 function
*/
app.crypto.PBKDF2 = function() {
app.crypto.PBKDF2 = function() {
/**
* PBKDF2-HMAC-SHA1 key derivation with a constant salt and 1000 iterations
@ -13,10 +14,15 @@ app.crypto.PBKDF2 = function() {
*/
this.getKey = function(password, keySize) {
var salt = CryptoJS.enc.Base64.parse("vbhmLjC+Ub6MSbhS6/CkOwxB25wvwRkSLP2DzDtYb+4="); // from random 256 bit value
var key = CryptoJS.PBKDF2(password, salt, { keySize: keySize/32, iterations: 1000 });
var key = CryptoJS.PBKDF2(password, salt, {
keySize: keySize / 32,
iterations: 1000
});
var keyBase64 = CryptoJS.enc.Base64.stringify(key);
return keyBase64;
};
};
};
}());

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* A wrapper for asymmetric OpenPGP encryption logic
*/
app.crypto.PGP = function(window, openpgp, util, server) {
app.crypto.PGP = function(window, openpgp, util, server) {
var self = this,
privateKey, // user's private key
@ -51,7 +52,9 @@ app.crypto.PGP = function(window, openpgp, util, server) {
*/
self.generateKeys = function(numBits) {
// check passphrase
if (!passphrase && passphrase !== '') { throw 'No passphrase set!'; }
if (!passphrase && passphrase !== '') {
throw 'No passphrase set!';
}
var userId = 'SafeWith.me User <anonymous@dunno.com>';
var keys = openpgp.generate_key_pair(1, numBits, userId, passphrase); // keytype 1=RSA
@ -66,7 +69,9 @@ app.crypto.PGP = function(window, openpgp, util, server) {
*/
self.importKeys = function(publicKeyArmored, privateKeyArmored) {
// check passphrase
if (!passphrase && passphrase !== '') { throw 'No passphrase set!'; }
if (!passphrase && passphrase !== '') {
throw 'No passphrase set!';
}
// store keys in html5 local storage
openpgp.keyring.importPrivateKey(privateKeyArmored, passphrase);
@ -105,7 +110,7 @@ app.crypto.PGP = function(window, openpgp, util, server) {
}
// read passphrase from local storage if no passphrase is specified
if(!passphrase && passphrase !== '') {
if (!passphrase && passphrase !== '') {
passphrase = window.sessionStorage.getItem(window.btoa(keyId) + 'Passphrase');
}
@ -132,14 +137,14 @@ app.crypto.PGP = function(window, openpgp, util, server) {
var keyId = publicKey.keyId;
var encodedKeyId = window.btoa(keyId);
var pubKey = {
keyId : encodedKeyId,
ownerEmail : email,
asciiArmored : publicKey.armored
keyId: encodedKeyId,
ownerEmail: email,
asciiArmored: publicKey.armored
};
var privKey = {
keyId : encodedKeyId,
ownerEmail : email,
asciiArmored : privateKey.armored
keyId: encodedKeyId,
ownerEmail: email,
asciiArmored: privateKey.armored
};
var jsonPublicKey = JSON.stringify(pubKey);
@ -164,6 +169,7 @@ app.crypto.PGP = function(window, openpgp, util, server) {
});
// then upload private key
function uploadPrivateKeys() {
server.xhr({
type: 'POST',
@ -202,6 +208,7 @@ app.crypto.PGP = function(window, openpgp, util, server) {
});
// get private key
function getPrivateKey(pubKey) {
server.xhr({
type: 'GET',
@ -210,7 +217,10 @@ app.crypto.PGP = function(window, openpgp, util, server) {
success: function(privKey) {
// import keys
self.importKeys(pubKey.asciiArmored, privKey.asciiArmored, email);
callback({ privateKey:privKey, publicKey:pubKey });
callback({
privateKey: privKey,
publicKey: pubKey
});
}
});
}
@ -220,7 +230,9 @@ app.crypto.PGP = function(window, openpgp, util, server) {
* Get the current user's private key
*/
self.getPrivateKey = function() {
if (!privateKey) { return undefined; }
if (!privateKey) {
return undefined;
}
return privateKey.armored;
};
@ -228,7 +240,9 @@ app.crypto.PGP = function(window, openpgp, util, server) {
* Get the current user's public key
*/
self.getPublicKey = function() {
if (!publicKey) { return undefined; }
if (!publicKey) {
return undefined;
}
return publicKey.armored;
};
@ -287,21 +301,27 @@ app.crypto.PGP = function(window, openpgp, util, server) {
var sesskey = null;
// Find the private (sub)key for the session key of the message
for (var i = 0; i< msg[0].sessionKeys.length; i++) {
for (var i = 0; i < msg[0].sessionKeys.length; i++) {
if (priv_key[0].privateKeyPacket.publicKey.getKeyId() == msg[0].sessionKeys[i].keyId.bytes) {
keymat = { key: priv_key[0], keymaterial: priv_key[0].privateKeyPacket};
keymat = {
key: priv_key[0],
keymaterial: priv_key[0].privateKeyPacket
};
sesskey = msg[0].sessionKeys[i];
break;
}
for (var j = 0; j < priv_key[0].subKeys.length; j++) {
if (priv_key[0].subKeys[j].publicKey.getKeyId() == msg[0].sessionKeys[i].keyId.bytes) {
keymat = { key: priv_key[0], keymaterial: priv_key[0].subKeys[j]};
keymat = {
key: priv_key[0],
keymaterial: priv_key[0].subKeys[j]
};
sesskey = msg[0].sessionKeys[i];
break;
}
}
}
if (keymat != null) {
if (keymat !== null) {
if (!keymat.keymaterial.decryptSecretMPIs(passphrase)) {
throw "Passphrase for secrect key was incorrect!";
}
@ -314,9 +334,12 @@ app.crypto.PGP = function(window, openpgp, util, server) {
}
};
};
};
}());
/**
* This function needs to be implemented, since it is used by the openpgp utils
*/
function showMessages(str) {}

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.crypto.Util = function(window, uuid) {
app.crypto.Util = function(window, uuid) {
/**
* Generates a new RFC 4122 version 4 compliant random UUID
@ -42,7 +43,12 @@ app.crypto.Util = function(window, uuid) {
// stringify to JSON before encryption
json = JSON.stringify(list[i].plaintext);
ct = aes.encrypt(json, list[i].key, list[i].iv);
outList.push({ id:list[i].id, ciphertext:ct, key:list[i].key, iv:list[i].iv });
outList.push({
id: list[i].id,
ciphertext: ct,
key: list[i].key,
iv: list[i].iv
});
}
return outList;
@ -60,7 +66,12 @@ app.crypto.Util = function(window, uuid) {
// decrypt JSON and parse to object literal
json = aes.decrypt(list[i].ciphertext, list[i].key, list[i].iv);
pt = JSON.parse(json);
outList.push({ id:list[i].id, plaintext:pt, key:list[i].key, iv:list[i].iv });
outList.push({
id: list[i].id,
plaintext: pt,
key: list[i].key,
iv: list[i].iv
});
}
return outList;
@ -83,7 +94,7 @@ app.crypto.Util = function(window, uuid) {
var b = new ArrayBuffer(str.length);
var buf = new Uint8Array(b);
for(var i = 0; i < b.byteLength; i++){
for (var i = 0; i < b.byteLength; i++) {
buf[i] = str.charCodeAt(i);
}
@ -97,7 +108,9 @@ app.crypto.Util = function(window, uuid) {
*/
this.arrBuf2Blob = function(buf, mimeType) {
var b = new Uint8Array(buf);
var blob = new Blob([b], {type: mimeType});
var blob = new Blob([b], {
type: mimeType
});
return blob;
};
@ -128,7 +141,7 @@ app.crypto.Util = function(window, uuid) {
var b = new Uint8Array(buf);
var str = '';
for(var i = 0; i < b.byteLength; i++){
for (var i = 0; i < b.byteLength; i++) {
str += String.fromCharCode(b[i]);
}
@ -143,11 +156,13 @@ app.crypto.Util = function(window, uuid) {
this.uint8Arr2BinStr = function(buf) {
var str = '';
for(var i = 0; i < buf.byteLength; i++){
for (var i = 0; i < buf.byteLength; i++) {
str += String.fromCharCode(buf[i]);
}
return str;
};
};
};
}());

View File

@ -1,10 +1,11 @@
'use strict';
(function() {
'use strict';
/**
/**
* High level storage api for handling syncing of data to
* and from the cloud.
*/
app.dao.CloudStorage = function(window, $) {
app.dao.CloudStorage = function(window, $) {
/**
* Lists the encrypted items
@ -25,7 +26,10 @@ app.dao.CloudStorage = function(window, $) {
callback(list);
},
error: function(xhr, textStatus, err) {
callback({error: err, status: textStatus});
callback({
error: err,
status: textStatus
});
}
});
};
@ -55,7 +59,10 @@ app.dao.CloudStorage = function(window, $) {
callback();
},
error: function(xhr, textStatus, err) {
callback({error: err, status: textStatus});
callback({
error: err,
status: textStatus
});
}
});
};
@ -78,21 +85,30 @@ app.dao.CloudStorage = function(window, $) {
success: function(fetchedKey) {
if ((!storedKey || !storedKey.key) && fetchedKey && fetchedKey.encryptedKey && fetchedKey.keyIV) {
// no local key... persist fetched key
keyStore.persist(storageId, { key: fetchedKey.encryptedKey, iv: fetchedKey.keyIV });
keyStore.persist(storageId, {
key: fetchedKey.encryptedKey,
iv: fetchedKey.keyIV
});
replaceCallback();
} else if (storedKey && fetchedKey && (storedKey.key !== fetchedKey.encryptedKey || storedKey.iv !== fetchedKey.keyIV)){
} else if (storedKey && fetchedKey && (storedKey.key !== fetchedKey.encryptedKey || storedKey.iv !== fetchedKey.keyIV)) {
// local and fetched keys are not equal
if (confirm('Swap local key?')) {
// replace local key with fetched key
keyStore.persist(storageId, { key: fetchedKey.encryptedKey, iv: fetchedKey.keyIV });
keyStore.persist(storageId, {
key: fetchedKey.encryptedKey,
iv: fetchedKey.keyIV
});
replaceCallback();
} else {
if (confirm('Swap cloud key?')) {
// upload local key to cloud
self.persistUserSecretKey(emailAddress, callback);
} else {
callback({error: 'err', status: 'Key not synced!'});
callback({
error: 'err',
status: 'Key not synced!'
});
}
}
@ -102,9 +118,14 @@ app.dao.CloudStorage = function(window, $) {
}
},
error: function(xhr, textStatus, err) {
callback({error: err, status: textStatus});
callback({
error: err,
status: textStatus
});
}
});
};
};
};
}());

View File

@ -1,12 +1,13 @@
'use strict';
(function() {
'use strict';
/**
/**
* High level storage api that handles all persistence on the device. If
* SQLcipher/SQLite is available, all data is securely persisted there,
* through transparent encryption. If not, the crypto API is
* used to encrypt data on the fly before persisting via a JSON store.
*/
app.dao.DeviceStorage = function(util, crypto, jsonDao, sqlcipherDao) {
app.dao.DeviceStorage = function(util, crypto, jsonDao, sqlcipherDao) {
/**
* Stores a list of encrypted items in the object store
@ -27,7 +28,10 @@ app.dao.DeviceStorage = function(util, crypto, jsonDao, sqlcipherDao) {
key = crypto.emailAddress + '_' + type + '_' + list[i].id;
}
items.push({ key:key, object:list[i] });
items.push({
key: key,
object: list[i]
});
}
jsonDao.batch(items, function() {
@ -60,4 +64,6 @@ app.dao.DeviceStorage = function(util, crypto, jsonDao, sqlcipherDao) {
jsonDao.clear(callback);
};
};
};
}());

View File

@ -1,10 +1,11 @@
'use strict';
(function() {
'use strict';
/**
/**
* A high-level Data-Access Api for handling Email synchronization
* between the cloud service and the device's local storage
*/
app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
/**
* Inits all dependencies
@ -38,9 +39,11 @@ app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
* Fetch an email with the following id
*/
this.getItem = function(folderName, itemId) {
var folder = this.account.get('folders').where({name: folderName})[0];
var folder = this.account.get('folders').where({
name: folderName
})[0];
var mail = _.find(folder.get('items').models, function(email) {
return email.id+'' === itemId+'';
return email.id + '' === itemId + '';
});
return mail;
};
@ -54,17 +57,21 @@ app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
var collection, folder, self = this;
// check if items are in memory already (account.folders model)
folder = this.account.get('folders').where({name: folderName})[0];
folder = this.account.get('folders').where({
name: folderName
})[0];
if (!folder) {
// get items from storage
devicestorage.listItems('email_' + folderName ,offset ,num, function(decryptedList) {
devicestorage.listItems('email_' + folderName, offset, num, function(decryptedList) {
// parse to backbone model collection
collection = new app.model.EmailCollection(decryptedList);
// cache collection in folder memory
if (decryptedList.length > 0) {
folder = new app.model.Folder({name: folderName});
folder = new app.model.Folder({
name: folderName
});
folder.set('items', collection);
self.account.get('folders').add(folder);
}
@ -98,7 +105,9 @@ app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
// persist encrypted list in device storage
devicestorage.storeEcryptedList(res, 'email_' + folderName, function() {
// remove cached folder in account model
folder = self.account.get('folders').where({name: folderName})[0];
folder = self.account.get('folders').where({
name: folderName
})[0];
if (folder) {
self.account.get('folders').remove(folder);
}
@ -107,4 +116,6 @@ app.dao.EmailDAO = function(_, crypto, devicestorage, cloudstorage) {
});
};
};
};
}());

View File

@ -1,41 +1,43 @@
'use strict';
(function() {
'use strict';
/**
/**
* Handles generic caching of JSON objects in a lawnchair adapter
*/
app.dao.LawnchairDAO = function(window) {
app.dao.LawnchairDAO = function(window) {
var db = new Lawnchair({
name: 'data-store'
}, function() {});
/**
* Create or update an object
*/
this.persist = function(key, object, callback) {
Lawnchair(function() {
this.save({ key:key, object:object }, callback);
});
db.save({
key: key,
object: object
}, callback);
};
/**
* Persist a bunch of items at once
*/
this.batch = function(list, callback) {
Lawnchair(function() {
this.batch(list, callback);
});
db.batch(list, callback);
};
/**
* Read a single item by its key
*/
this.read = function(key, callback) {
Lawnchair(function() {
this.get(key, function(o) {
db.get(key, function(o) {
if (o) {
callback(o.object);
} else {
callback(null);
}
});
});
};
/**
@ -45,13 +47,12 @@ app.dao.LawnchairDAO = function(window) {
* @param num [Number] The number of items to fetch (null means fetch all)
*/
this.list = function(type, offset, num, callback) {
var i, list = [], matchingKeys = [], parts, timeStr, time;
Lawnchair(function() {
var self = this;
var i, list = [],
matchingKeys = [],
parts, timeStr, time;
// get all keys
this.keys(function(keys) {
db.keys(function(keys) {
// check if key begins with type
for (i = 0; i < keys.length; i++) {
@ -63,7 +64,7 @@ app.dao.LawnchairDAO = function(window) {
// sort keys by type and date
matchingKeys = _.sortBy(matchingKeys, function(key) {
parts = key.split('_');
timeStr = parts[parts.length-2];
timeStr = parts[parts.length - 2];
time = parseInt(timeStr, 10);
return time;
});
@ -87,7 +88,7 @@ app.dao.LawnchairDAO = function(window) {
}
// fetch all items from data-store with matching key
self.get(matchingKeys, function(matchingList) {
db.get(matchingKeys, function(matchingList) {
for (i = 0; i < matchingList.length; i++) {
list.push(matchingList[i].object);
}
@ -97,25 +98,22 @@ app.dao.LawnchairDAO = function(window) {
});
});
});
};
/**
* Removes an object liter from local storage by its key (delete)
*/
this.remove = function(key, callback) {
Lawnchair(function() {
this.remove(key, callback);
});
db.remove(key, callback);
};
/**
* Clears the whole local storage cache
*/
this.clear = function(callback) {
Lawnchair(function() {
this.nuke(callback);
});
db.nuke(callback);
};
};
};
}());

View File

@ -1,9 +1,10 @@
'use strict';
(function() {
'use strict';
/**
/**
* Handles generic caching of JSON objects in LocalStorage
*/
app.dao.LocalStorageDAO = function(window) {
app.dao.LocalStorageDAO = function(window) {
/**
* Stringifies an object literal to JSON and perists it
@ -53,4 +54,6 @@ app.dao.LocalStorageDAO = function(window) {
window.localStorage.clear();
};
};
};
}());

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.model.Account = Backbone.Model.extend({
app.model.Account = Backbone.Model.extend({
defaults: {
emailAddress: null,
@ -9,17 +10,18 @@ app.model.Account = Backbone.Model.extend({
folders: null
},
initialize: function () {
initialize: function() {
this.set('folders', new app.model.FolderCollection());
}
});
});
app.model.AccountCollection = Backbone.Collection.extend({
app.model.AccountCollection = Backbone.Collection.extend({
model: app.model.Account,
findByName: function (key) {
}
findByName: function(key) {}
});
});
}());

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.model.Email = Backbone.Model.extend({
app.model.Email = Backbone.Model.extend({
defaults: {
id: null,
@ -13,7 +14,7 @@ app.model.Email = Backbone.Model.extend({
sentDate: null
},
initialize: function () {
initialize: function() {
// decode body
try {
var decodedBody = window.atob(this.get('body'));
@ -23,13 +24,14 @@ app.model.Email = Backbone.Model.extend({
}
}
});
});
app.model.EmailCollection = Backbone.Collection.extend({
app.model.EmailCollection = Backbone.Collection.extend({
model: app.model.Email,
findByName: function (key) {
}
findByName: function(key) {}
});
});
}());

View File

@ -1,22 +1,23 @@
'use strict';
(function() {
'use strict';
app.model.Folder = Backbone.Model.extend({
app.model.Folder = Backbone.Model.extend({
defaults: {
name: null,
items: null
},
initialize: function () {
}
initialize: function() {}
});
});
app.model.FolderCollection = Backbone.Collection.extend({
app.model.FolderCollection = Backbone.Collection.extend({
model: app.model.Folder,
findByName: function (key) {
}
findByName: function(key) {}
});
});
}());

View File

@ -1,13 +1,16 @@
'use strict';
(function() {
'use strict';
app.view.AccountsView = Backbone.View.extend({
app.view.AccountsView = Backbone.View.extend({
initialize:function () {
initialize: function() {
this.template = _.template(app.util.tpl.get('accounts'));
},
render:function (eventName) {
render: function(eventName) {
$(this.el).html(this.template());
return this;
}
});
});
}());

View File

@ -1,13 +1,16 @@
'use strict';
(function() {
'use strict';
app.view.ComposeView = Backbone.View.extend({
app.view.ComposeView = Backbone.View.extend({
initialize:function () {
initialize: function() {
this.template = _.template(app.util.tpl.get('compose'));
},
render:function (eventName) {
render: function(eventName) {
$(this.el).html(this.template());
return this;
}
});
});
}());

View File

@ -1,12 +1,13 @@
'use strict';
(function() {
'use strict';
app.view.FolderListView = Backbone.View.extend({
app.view.FolderListView = Backbone.View.extend({
initialize:function () {
initialize: function() {
this.template = _.template(app.util.tpl.get('folderlist'));
},
render:function (eventName) {
render: function(eventName) {
var page = $(this.el);
page.html(this.template(this.options));
@ -20,4 +21,6 @@ app.view.FolderListView = Backbone.View.extend({
return this;
}
});
});
}());

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.view.LoginView = Backbone.View.extend({
app.view.LoginView = Backbone.View.extend({
initialize: function(args) {
this.template = _.template(app.util.tpl.get('login'));
@ -8,7 +9,8 @@ app.view.LoginView = Backbone.View.extend({
},
render: function(eventName) {
var self = this, page = $(this.el);
var self = this,
page = $(this.el);
page.html(this.template());
page.attr('data-theme', 'a');
@ -32,10 +34,16 @@ app.view.LoginView = Backbone.View.extend({
});
// show loading msg during init
$.mobile.loading('show', { text: 'Unlocking...', textVisible: true, theme: 'c' });
$.mobile.loading('show', {
text: 'Unlocking...',
textVisible: true,
theme: 'c'
});
this.dao.init(account, password, function() {
$.mobile.loading('hide');
window.location = '#accounts/' + account.get('emailAddress') + '/folders';
});
}
});
});
}());

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.view.MessageListView = Backbone.View.extend({
app.view.MessageListView = Backbone.View.extend({
initialize: function(args) {
this.template = _.template(app.util.tpl.get('messagelist'));
@ -27,7 +28,10 @@ app.view.MessageListView = Backbone.View.extend({
syncFolder: function() {
var self = this;
$.mobile.loading('show', { text: 'Syncing...', textVisible: true });
$.mobile.loading('show', {
text: 'Syncing...',
textVisible: true
});
// sync from cloud
this.dao.syncFromCloud(this.folder, function(res) {
$.mobile.loading('hide');
@ -52,7 +56,10 @@ app.view.MessageListView = Backbone.View.extend({
list = page.find('#message-list'),
listItemArgs, i, email;
$.mobile.loading('show', { text: 'decrypting...', textVisible: true });
$.mobile.loading('show', {
text: 'decrypting...',
textVisible: true
});
this.dao.listItems(this.folder, 0, 10, function(collection) {
// clear list
list.html('');
@ -60,7 +67,11 @@ app.view.MessageListView = Backbone.View.extend({
// append items to list in reverse order so mails with the most recent date will be displayed first
for (i = collection.models.length - 1; i >= 0; i--) {
email = collection.at(i);
listItemArgs = {account: self.options.account, folder: self.folder, model: email};
listItemArgs = {
account: self.options.account,
folder: self.folder,
model: email
};
list.append(new app.view.MessageListItemView(listItemArgs).render().el);
}
@ -70,4 +81,6 @@ app.view.MessageListView = Backbone.View.extend({
});
}
});
});
}());

View File

@ -1,14 +1,15 @@
'use strict';
(function() {
'use strict';
app.view.MessageListItemView = Backbone.View.extend({
app.view.MessageListItemView = Backbone.View.extend({
tagName:"li",
tagName: "li",
initialize:function () {
initialize: function() {
this.template = _.template(app.util.tpl.get('messagelistitem'));
},
render:function (eventName) {
render: function(eventName) {
var params = this.model.toJSON();
params.account = this.options.account;
params.folder = this.options.folder;
@ -21,4 +22,6 @@ app.view.MessageListItemView = Backbone.View.extend({
$(this.el).html(this.template(params));
return this;
}
});
});
}());

View File

@ -1,6 +1,7 @@
'use strict';
(function() {
'use strict';
app.view.ReadView = Backbone.View.extend({
app.view.ReadView = Backbone.View.extend({
initialize: function(args) {
this.template = _.template(app.util.tpl.get('read'));
@ -29,4 +30,6 @@ app.view.ReadView = Backbone.View.extend({
iframeDoc.close();
}
});
});
}());