mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 10:22:18 -05:00
started refacrtoring for sandboxed iframe
This commit is contained in:
parent
b580506465
commit
71d6d6c799
45
.jshintrc
Normal file
45
.jshintrc
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"strict": true,
|
||||
"globalstrict": true,
|
||||
"jquery": true,
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"camelcase": true,
|
||||
"nonew": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"newcap": true,
|
||||
"regexp": true,
|
||||
"evil": true,
|
||||
"eqnull": true,
|
||||
"expr": true,
|
||||
"trailing": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
|
||||
"predef": [
|
||||
"self",
|
||||
"console",
|
||||
"importScripts",
|
||||
"cryptoLib",
|
||||
"Backbone",
|
||||
"forge",
|
||||
"uuid",
|
||||
"Lawnchair",
|
||||
"_",
|
||||
"process",
|
||||
"describe",
|
||||
"it",
|
||||
"chai",
|
||||
"ok",
|
||||
"equal",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"chrome"
|
||||
],
|
||||
|
||||
"globals": {
|
||||
"app": true
|
||||
}
|
||||
}
|
12
Gruntfile.js
12
Gruntfile.js
@ -42,15 +42,19 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
jshint: {
|
||||
all: ['Gruntfile.js', 'src/js/**/*.js']
|
||||
all: ['Gruntfile.js', 'src/js/**/*.js'],
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
}
|
||||
},
|
||||
|
||||
qunit: {
|
||||
all: {
|
||||
options: {
|
||||
urls: [
|
||||
'http://localhost:<%= connect.test.options.port %>/test/unit/index.html',
|
||||
'http://localhost:<%= connect.test.options.port %>/test/integration/index.html']
|
||||
'http://localhost:<%= connect.test.options.port %>/test/unit/index.html',
|
||||
'http://localhost:<%= connect.test.options.port %>/test/integration/index.html'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,7 +67,7 @@ module.exports = function(grunt) {
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('dev', ['connect:dev']);
|
||||
grunt.registerTask('test', ['jshint', 'connect:test', 'qunit']);
|
||||
grunt.registerTask('test', ['connect:test', 'qunit']);
|
||||
grunt.registerTask('prod', ['connect:prod']);
|
||||
|
||||
};
|
10
src/background.js
Normal file
10
src/background.js
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
chrome.app.runtime.onLaunched.addListener(function() {
|
||||
chrome.app.window.create('index.html', {
|
||||
'bounds': {
|
||||
'width': 805,
|
||||
'height': 620
|
||||
}
|
||||
});
|
||||
});
|
@ -41,4 +41,14 @@ textarea.ui-input-text {
|
||||
max-width: 400px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
div#sandboxDiv iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Mail</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/jquery.mobile-1.2.0.min.css"/>
|
||||
<link rel="stylesheet" href="css/styles.css"/>
|
||||
|
||||
<!-- The Scripts -->
|
||||
@ -13,9 +12,7 @@
|
||||
<script src="lib/jquery-1.8.2.min.js"></script>
|
||||
<script src="lib/underscore-1.4.4.min.js"></script>
|
||||
<script src="lib/backbone-1.0.0.min.js"></script>
|
||||
<script src="js/jqm-config.js"></script>
|
||||
<script src="lib/jquery.mobile-1.2.0.min.js"></script>
|
||||
<script src="lib/lawnchair/lawnchair-git.min.js"></script>
|
||||
<script src="lib/lawnchair/lawnchair-git.js"></script>
|
||||
<script src="lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js"></script>
|
||||
<script src="lib/lawnchair/lawnchair-adapter-indexed-db-git.js"></script>
|
||||
|
||||
@ -41,18 +38,14 @@
|
||||
<script src="js/dao/cloudstorage-dao.js"></script>
|
||||
<script src="js/dao/keychain-dao.js"></script>
|
||||
<script src="js/dao/email-dao.js"></script>
|
||||
|
||||
<script src="js/view/login-view.js"></script>
|
||||
<script src="js/view/compose-view.js"></script>
|
||||
<script src="js/view/messagelist-view.js"></script>
|
||||
<script src="js/view/messagelistitem-view.js"></script>
|
||||
<script src="js/view/folderlist-view.js"></script>
|
||||
<script src="js/view/read-view.js"></script>
|
||||
|
||||
<script src="js/app-router.js"></script>
|
||||
<script src="js/loader.js"></script>
|
||||
|
||||
<script src="js/window-loader.js"></script>
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
<body>
|
||||
<div id="sandboxDiv">
|
||||
<iframe id="sandboxFrame" src="sandbox.html" sandbox="allow-scripts" frameborder="0"></iframe>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -16,13 +16,7 @@
|
||||
|
||||
login: function() {
|
||||
// init email dao and dependencies
|
||||
var util = new cryptoLib.Util(window, uuid);
|
||||
var crypto = new app.crypto.Crypto(window, util);
|
||||
var cloudstorage = new app.dao.CloudStorage(window, $);
|
||||
var jsonDao = new app.dao.LawnchairDAO(Lawnchair);
|
||||
var devicestorage = new app.dao.DeviceStorage(util, crypto, jsonDao, null);
|
||||
var keychain = new app.dao.KeychainDAO(jsonDao, cloudstorage);
|
||||
this.emailDao = new app.dao.EmailDAO(jsonDao, crypto, devicestorage, cloudstorage, util, keychain);
|
||||
this.emailDao = null;
|
||||
|
||||
var loginView = new app.view.LoginView({
|
||||
dao: this.emailDao
|
||||
|
37
src/js/sandbox-loader.js
Normal file
37
src/js/sandbox-loader.js
Normal file
@ -0,0 +1,37 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The Template Loader. Used to asynchronously load templates located in separate .html files
|
||||
*/
|
||||
app.util.tpl = {
|
||||
|
||||
// Hash of preloaded templates for the app
|
||||
templates: {},
|
||||
|
||||
// Get template by name from hash of preloaded templates
|
||||
get: function(name) {
|
||||
return this.templates[name];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load templates and start the application
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
console.log('sandbox loaded');
|
||||
|
||||
window.onmessage = function(e) {
|
||||
app.util.tpl.templates = e.data;
|
||||
|
||||
// remember references to main window
|
||||
window.mainWindow = e.source;
|
||||
window.mainWindowOrigin = e.origin;
|
||||
|
||||
var router = new app.Router();
|
||||
Backbone.history.start();
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
@ -40,15 +40,21 @@
|
||||
textVisible: true,
|
||||
theme: 'c'
|
||||
});
|
||||
this.dao.init(account, password, function(err) {
|
||||
$.mobile.loading('hide');
|
||||
if (err) {
|
||||
window.alert(err.errMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location = '#accounts/' + account.get('emailAddress') + '/folders';
|
||||
});
|
||||
window.location = '#accounts/test@example.com/folders';
|
||||
window.mainWindow.postMessage({
|
||||
cmd: 'hello back from sandbox'
|
||||
}, window.mainWindowOrigin);
|
||||
|
||||
// this.dao.init(account, password, function(err) {
|
||||
// $.mobile.loading('hide');
|
||||
// if (err) {
|
||||
// window.alert(err.errMsg);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// window.location = '#accounts/' + account.get('emailAddress') + '/folders';
|
||||
// });
|
||||
}
|
||||
});
|
||||
|
||||
|
77
src/js/window-loader.js
Normal file
77
src/js/window-loader.js
Normal file
@ -0,0 +1,77 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The Template Loader. Used to asynchronously load templates located in separate .html files
|
||||
*/
|
||||
app.util.tpl = {
|
||||
|
||||
// Hash of preloaded templates for the app
|
||||
templates: {},
|
||||
|
||||
// Recursively pre-load all the templates for the app.
|
||||
loadTemplates: function(names, callback) {
|
||||
var that = this;
|
||||
|
||||
var loadTemplate = function(index) {
|
||||
var name = names[index];
|
||||
console.log('Loading template: ' + name);
|
||||
$.get('tpl/' + name + '.html', function(data) {
|
||||
that.templates[name] = data;
|
||||
index++;
|
||||
if (index < names.length) {
|
||||
loadTemplate(index);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadTemplate(0);
|
||||
},
|
||||
|
||||
// Get template by name from hash of preloaded templates
|
||||
get: function(name) {
|
||||
return this.templates[name];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load templates and start the application
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
// are we running in native app or in browser?
|
||||
var isBrowser = false;
|
||||
if (document.URL.indexOf("http") === 0 || document.URL.indexOf("chrome") === 0) {
|
||||
isBrowser = true;
|
||||
}
|
||||
|
||||
if (!isBrowser) {
|
||||
document.addEventListener("deviceready", onDeviceReady, false);
|
||||
} else {
|
||||
onDeviceReady();
|
||||
}
|
||||
|
||||
function onDeviceReady() {
|
||||
console.log('Starting in Browser: ' + isBrowser);
|
||||
app.util.tpl.loadTemplates([
|
||||
'login',
|
||||
'compose',
|
||||
'folderlist',
|
||||
'messagelist',
|
||||
'messagelistitem',
|
||||
'read'
|
||||
], function() {
|
||||
// set listener for events from sandbox
|
||||
window.onmessage = function(e) {
|
||||
console.log(e.data);
|
||||
};
|
||||
// init sandbox ui
|
||||
var sandbox = document.getElementById('sandboxFrame').contentWindow;
|
||||
sandbox.postMessage(app.util.tpl.templates, '*');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
293
src/lib/lawnchair/lawnchair-git.js
Normal file
293
src/lib/lawnchair/lawnchair-git.js
Normal file
@ -0,0 +1,293 @@
|
||||
/**
|
||||
* Lawnchair!
|
||||
* ---
|
||||
* clientside json store
|
||||
*
|
||||
*/
|
||||
var Lawnchair = function (options, callback) {
|
||||
// ensure Lawnchair was called as a constructor
|
||||
if (!(this instanceof Lawnchair)) return new Lawnchair(options, callback);
|
||||
|
||||
// lawnchair requires json
|
||||
if (!JSON) throw 'JSON unavailable! Include http://www.json.org/json2.js to fix.'
|
||||
// options are optional; callback is not
|
||||
if (arguments.length <= 2) {
|
||||
callback = (typeof arguments[0] === 'function') ? arguments[0] : arguments[1];
|
||||
options = (typeof arguments[0] === 'function') ? {} : arguments[0] || {};
|
||||
} else {
|
||||
throw 'Incorrect # of ctor args!'
|
||||
}
|
||||
|
||||
// default configuration
|
||||
this.record = options.record || 'record' // default for records
|
||||
this.name = options.name || 'records' // default name for underlying store
|
||||
|
||||
// mixin first valid adapter
|
||||
var adapter
|
||||
// if the adapter is passed in we try to load that only
|
||||
if (options.adapter) {
|
||||
|
||||
// the argument passed should be an array of prefered adapters
|
||||
// if it is not, we convert it
|
||||
if(typeof(options.adapter) === 'string'){
|
||||
options.adapter = [options.adapter];
|
||||
}
|
||||
|
||||
// iterates over the array of passed adapters
|
||||
for(var j = 0, k = options.adapter.length; j < k; j++){
|
||||
|
||||
// itirates over the array of available adapters
|
||||
for (var i = Lawnchair.adapters.length-1; i >= 0; i--) {
|
||||
if (Lawnchair.adapters[i].adapter === options.adapter[j]) {
|
||||
adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined;
|
||||
if (adapter) break
|
||||
}
|
||||
}
|
||||
if (adapter) break
|
||||
}
|
||||
|
||||
// otherwise find the first valid adapter for this env
|
||||
}
|
||||
else {
|
||||
for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) {
|
||||
adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined
|
||||
if (adapter) break
|
||||
}
|
||||
}
|
||||
|
||||
// we have failed
|
||||
if (!adapter) throw 'No valid adapter.'
|
||||
|
||||
// yay! mixin the adapter
|
||||
for (var j in adapter)
|
||||
this[j] = adapter[j]
|
||||
|
||||
// call init for each mixed in plugin
|
||||
for (var i = 0, l = Lawnchair.plugins.length; i < l; i++)
|
||||
Lawnchair.plugins[i].call(this)
|
||||
|
||||
// init the adapter
|
||||
this.init(options, callback)
|
||||
}
|
||||
|
||||
Lawnchair.adapters = []
|
||||
|
||||
/**
|
||||
* queues an adapter for mixin
|
||||
* ===
|
||||
* - ensures an adapter conforms to a specific interface
|
||||
*
|
||||
*/
|
||||
Lawnchair.adapter = function (id, obj) {
|
||||
// add the adapter id to the adapter obj
|
||||
// ugly here for a cleaner dsl for implementing adapters
|
||||
obj['adapter'] = id
|
||||
// methods required to implement a lawnchair adapter
|
||||
var implementing = 'adapter valid init keys save batch get exists all remove nuke'.split(' ')
|
||||
, indexOf = this.prototype.indexOf
|
||||
// mix in the adapter
|
||||
for (var i in obj) {
|
||||
if (indexOf(implementing, i) === -1) throw 'Invalid adapter! Nonstandard method: ' + i
|
||||
}
|
||||
// if we made it this far the adapter interface is valid
|
||||
// insert the new adapter as the preferred adapter
|
||||
Lawnchair.adapters.splice(0,0,obj)
|
||||
}
|
||||
|
||||
Lawnchair.plugins = []
|
||||
|
||||
/**
|
||||
* generic shallow extension for plugins
|
||||
* ===
|
||||
* - if an init method is found it registers it to be called when the lawnchair is inited
|
||||
* - yes we could use hasOwnProp but nobody here is an asshole
|
||||
*/
|
||||
Lawnchair.plugin = function (obj) {
|
||||
for (var i in obj)
|
||||
i === 'init' ? Lawnchair.plugins.push(obj[i]) : this.prototype[i] = obj[i]
|
||||
}
|
||||
|
||||
/**
|
||||
* helpers
|
||||
*
|
||||
*/
|
||||
Lawnchair.prototype = {
|
||||
|
||||
isArray: Array.isArray || function(o) { return Object.prototype.toString.call(o) === '[object Array]' },
|
||||
|
||||
/**
|
||||
* this code exists for ie8... for more background see:
|
||||
* http://www.flickr.com/photos/westcoastlogic/5955365742/in/photostream
|
||||
*/
|
||||
indexOf: function(ary, item, i, l) {
|
||||
if (ary.indexOf) return ary.indexOf(item)
|
||||
for (i = 0, l = ary.length; i < l; i++) if (ary[i] === item) return i
|
||||
return -1
|
||||
},
|
||||
|
||||
// awesome shorthand callbacks as strings. this is shameless theft from dojo.
|
||||
lambda: function (callback) {
|
||||
return this.fn(this.record, callback)
|
||||
},
|
||||
|
||||
// first stab at named parameters for terse callbacks; dojo: first != best // ;D
|
||||
fn: function (name, callback) {
|
||||
return typeof callback == 'string' ? new Function(name, callback) : callback
|
||||
},
|
||||
|
||||
// returns a unique identifier (by way of Backbone.localStorage.js)
|
||||
// TODO investigate smaller UUIDs to cut on storage cost
|
||||
uuid: function () {
|
||||
var S4 = function () {
|
||||
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
||||
}
|
||||
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
|
||||
},
|
||||
|
||||
// a classic iterator
|
||||
each: function (callback) {
|
||||
var cb = this.lambda(callback)
|
||||
// iterate from chain
|
||||
if (this.__results) {
|
||||
for (var i = 0, l = this.__results.length; i < l; i++) cb.call(this, this.__results[i], i)
|
||||
}
|
||||
// otherwise iterate the entire collection
|
||||
else {
|
||||
this.all(function(r) {
|
||||
for (var i = 0, l = r.length; i < l; i++) cb.call(this, r[i], i)
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
// --
|
||||
};
|
||||
// window.name code courtesy Remy Sharp: http://24ways.org/2009/breaking-out-the-edges-of-the-browser
|
||||
Lawnchair.adapter('window-name', (function() {
|
||||
if (typeof window==='undefined') {
|
||||
window = { top: { } }; // node/optimizer compatibility
|
||||
}
|
||||
|
||||
// edited from the original here by elsigh
|
||||
// Some sites store JSON data in window.top.name, but some folks (twitter on iPad)
|
||||
// put simple strings in there - we should make sure not to cause a SyntaxError.
|
||||
var data = {}
|
||||
try {
|
||||
data = JSON.parse(window.top.name)
|
||||
} catch (e) {}
|
||||
|
||||
|
||||
return {
|
||||
|
||||
valid: function () {
|
||||
return typeof window.top.name != 'undefined'
|
||||
},
|
||||
|
||||
init: function (options, callback) {
|
||||
data[this.name] = data[this.name] || {index:[],store:{}}
|
||||
this.index = data[this.name].index
|
||||
this.store = data[this.name].store
|
||||
this.fn(this.name, callback).call(this, this)
|
||||
return this
|
||||
},
|
||||
|
||||
keys: function (callback) {
|
||||
this.fn('keys', callback).call(this, this.index)
|
||||
return this
|
||||
},
|
||||
|
||||
save: function (obj, cb) {
|
||||
// data[key] = value + ''; // force to string
|
||||
// window.top.name = JSON.stringify(data);
|
||||
var key = obj.key || this.uuid()
|
||||
this.exists(key, function(exists) {
|
||||
if (!exists) {
|
||||
if (obj.key) delete obj.key
|
||||
this.index.push(key)
|
||||
}
|
||||
this.store[key] = obj
|
||||
|
||||
try {
|
||||
window.top.name = JSON.stringify(data) // TODO wow, this is the only diff from the memory adapter
|
||||
} catch(e) {
|
||||
// restore index/store to previous value before JSON exception
|
||||
if (!exists) {
|
||||
this.index.pop();
|
||||
delete this.store[key];
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
obj.key = key
|
||||
this.lambda(cb).call(this, obj)
|
||||
}
|
||||
})
|
||||
return this
|
||||
},
|
||||
|
||||
batch: function (objs, cb) {
|
||||
var r = []
|
||||
for (var i = 0, l = objs.length; i < l; i++) {
|
||||
this.save(objs[i], function(record) {
|
||||
r.push(record)
|
||||
})
|
||||
}
|
||||
if (cb) this.lambda(cb).call(this, r)
|
||||
return this
|
||||
},
|
||||
|
||||
get: function (keyOrArray, cb) {
|
||||
var r;
|
||||
if (this.isArray(keyOrArray)) {
|
||||
r = []
|
||||
for (var i = 0, l = keyOrArray.length; i < l; i++) {
|
||||
r.push(this.store[keyOrArray[i]])
|
||||
}
|
||||
} else {
|
||||
r = this.store[keyOrArray]
|
||||
if (r) r.key = keyOrArray
|
||||
}
|
||||
if (cb) this.lambda(cb).call(this, r)
|
||||
return this
|
||||
},
|
||||
|
||||
exists: function (key, cb) {
|
||||
this.lambda(cb).call(this, !!(this.store[key]))
|
||||
return this
|
||||
},
|
||||
|
||||
all: function (cb) {
|
||||
var r = []
|
||||
for (var i = 0, l = this.index.length; i < l; i++) {
|
||||
var obj = this.store[this.index[i]]
|
||||
obj.key = this.index[i]
|
||||
r.push(obj)
|
||||
}
|
||||
this.fn(this.name, cb).call(this, r)
|
||||
return this
|
||||
},
|
||||
|
||||
remove: function (keyOrArray, cb) {
|
||||
var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]
|
||||
for (var i = 0, l = del.length; i < l; i++) {
|
||||
var key = del[i].key ? del[i].key : del[i]
|
||||
var where = this.indexOf(this.index, key)
|
||||
if (where < 0) continue /* key not present */
|
||||
delete this.store[key]
|
||||
this.index.splice(where, 1)
|
||||
}
|
||||
window.top.name = JSON.stringify(data)
|
||||
if (cb) this.lambda(cb).call(this)
|
||||
return this
|
||||
},
|
||||
|
||||
nuke: function (cb) {
|
||||
this.store = data[this.name].store = {}
|
||||
this.index = data[this.name].index = []
|
||||
window.top.name = JSON.stringify(data)
|
||||
if (cb) this.lambda(cb).call(this)
|
||||
return this
|
||||
}
|
||||
}
|
||||
/////
|
||||
})())
|
14
src/lib/lawnchair/lawnchair-git.min.js
vendored
14
src/lib/lawnchair/lawnchair-git.min.js
vendored
@ -1,14 +0,0 @@
|
||||
var Lawnchair=function(e,h){if(!(this instanceof Lawnchair))return new Lawnchair(e,h);if(!JSON)throw"JSON unavailable! Include http://www.json.org/json2.js to fix.";if(arguments.length<=2){h=typeof arguments[0]==="function"?arguments[0]:arguments[1];e=typeof arguments[0]==="function"?{}:arguments[0]||{}}else throw"Incorrect # of ctor args!";this.record=e.record||"record";this.name=e.name||"records";var a;if(e.adapter){if(typeof e.adapter==="string")e.adapter=[e.adapter];for(var b=0,c=e.adapter.length;b<
|
||||
c;b++){for(var d=Lawnchair.adapters.length-1;d>=0;d--)if(Lawnchair.adapters[d].adapter===e.adapter[b])if(a=Lawnchair.adapters[d].valid()?Lawnchair.adapters[d]:undefined)break;if(a)break}}else{d=0;for(c=Lawnchair.adapters.length;d<c;d++)if(a=Lawnchair.adapters[d].valid()?Lawnchair.adapters[d]:undefined)break}if(!a)throw"No valid adapter.";for(b in a)this[b]=a[b];d=0;for(c=Lawnchair.plugins.length;d<c;d++)Lawnchair.plugins[d].call(this);this.init(e,h)};Lawnchair.adapters=[];
|
||||
Lawnchair.adapter=function(e,h){h.adapter=e;var a="adapter valid init keys save batch get exists all remove nuke".split(" "),b=this.prototype.indexOf,c;for(c in h)if(b(a,c)===-1)throw"Invalid adapter! Nonstandard method: "+c;Lawnchair.adapters.splice(0,0,h)};Lawnchair.plugins=[];Lawnchair.plugin=function(e){for(var h in e)h==="init"?Lawnchair.plugins.push(e[h]):this.prototype[h]=e[h]};
|
||||
Lawnchair.prototype={isArray:Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"},indexOf:function(e,h,a,b){if(e.indexOf)return e.indexOf(h);a=0;for(b=e.length;a<b;a++)if(e[a]===h)return a;return-1},lambda:function(e){return this.fn(this.record,e)},fn:function(e,h){return typeof h=="string"?new Function(e,h):h},uuid:function(){var e=function(){return((1+Math.random())*65536|0).toString(16).substring(1)};return e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e()},each:function(e){var h=
|
||||
this.lambda(e);if(this.__results){e=0;for(var a=this.__results.length;e<a;e++)h.call(this,this.__results[e],e)}else this.all(function(b){for(var c=0,d=b.length;c<d;c++)h.call(this,b[c],c)});return this}};
|
||||
Lawnchair.adapter("window-name",function(){if(typeof window==="undefined")window={top:{}};var e={};try{e=JSON.parse(window.top.name)}catch(h){}return{valid:function(){return typeof window.top.name!="undefined"},init:function(a,b){e[this.name]=e[this.name]||{index:[],store:{}};this.index=e[this.name].index;this.store=e[this.name].store;this.fn(this.name,b).call(this,this);return this},keys:function(a){this.fn("keys",a).call(this,this.index);return this},save:function(a,b){var c=a.key||this.uuid();
|
||||
this.exists(c,function(d){if(!d){a.key&&delete a.key;this.index.push(c)}this.store[c]=a;try{window.top.name=JSON.stringify(e)}catch(g){if(!d){this.index.pop();delete this.store[c]}throw g;}if(b){a.key=c;this.lambda(b).call(this,a)}});return this},batch:function(a,b){for(var c=[],d=0,g=a.length;d<g;d++)this.save(a[d],function(f){c.push(f)});b&&this.lambda(b).call(this,c);return this},get:function(a,b){var c;if(this.isArray(a)){c=[];for(var d=0,g=a.length;d<g;d++)c.push(this.store[a[d]])}else if(c=
|
||||
this.store[a])c.key=a;b&&this.lambda(b).call(this,c);return this},exists:function(a,b){this.lambda(b).call(this,!!this.store[a]);return this},all:function(a){for(var b=[],c=0,d=this.index.length;c<d;c++){var g=this.store[this.index[c]];g.key=this.index[c];b.push(g)}this.fn(this.name,a).call(this,b);return this},remove:function(a,b){for(var c=this.isArray(a)?a:[a],d=0,g=c.length;d<g;d++){var f=c[d].key?c[d].key:c[d],i=this.indexOf(this.index,f);if(!(i<0)){delete this.store[f];this.index.splice(i,1)}}window.top.name=
|
||||
JSON.stringify(e);b&&this.lambda(b).call(this);return this},nuke:function(a){this.store=e[this.name].store={};this.index=e[this.name].index=[];window.top.name=JSON.stringify(e);a&&this.lambda(a).call(this);return this}}}());
|
||||
Lawnchair.adapter("dom",function(){var e=window.localStorage,h=function(a){return{key:a+"._index_",all:function(){var b=e.getItem(JSON.stringify(this.key));if(b)b=JSON.parse(b);b===null&&e.setItem(JSON.stringify(this.key),JSON.stringify([]));return JSON.parse(e.getItem(JSON.stringify(this.key)))},add:function(b){var c=this.all();c.push(b);e.setItem(JSON.stringify(this.key),JSON.stringify(c))},del:function(b){for(var c=this.all(),d=[],g=0,f=c.length;g<f;g++)c[g]!=b&&d.push(c[g]);e.setItem(JSON.stringify(this.key),
|
||||
JSON.stringify(d))},find:function(b){for(var c=this.all(),d=0,g=c.length;d<g;d++)if(b===c[d])return d;return false}}};return{valid:function(){return!!e&&function(){var a=true,b=Math.random();try{e.setItem(b,b)}catch(c){a=false}e.removeItem(b);return a}()},init:function(a,b){this.indexer=h(this.name);b&&this.fn(this.name,b).call(this,this)},save:function(a,b){var c=a.key?this.name+"."+a.key:this.name+"."+this.uuid();delete a.key;e.setItem(c,JSON.stringify(a));this.indexer.find(c)===false&&this.indexer.add(c);
|
||||
a.key=c.slice(this.name.length+1);b&&this.lambda(b).call(this,a);return this},batch:function(a,b){for(var c=[],d=0,g=a.length;d<g;d++)this.save(a[d],function(f){c.push(f)});b&&this.lambda(b).call(this,c);return this},keys:function(a){if(a){var b=this.name,c=this.indexer.all(),d=[];if(Array.prototype.map)d=c.map(function(f){return f.replace(b+".","")});else for(var g in c)d.push(g.replace(b+".",""));this.fn("keys",a).call(this,d)}return this},get:function(a,b){if(this.isArray(a)){for(var c=[],d=0,
|
||||
g=a.length;d<g;d++){var f=this.name+"."+a[d];if(f=e.getItem(f)){f=JSON.parse(f);f.key=a[d]}c.push(f)}b&&this.lambda(b).call(this,c)}else{f=this.name+"."+a;if(f=e.getItem(f)){f=JSON.parse(f);f.key=a}b&&this.lambda(b).call(this,f)}return this},exists:function(a,b){var c=this.indexer.find(this.name+"."+a)===false?false:true;this.lambda(b).call(this,c);return this},all:function(a){for(var b=this.indexer.all(),c=[],d,g,f=0,i=b.length;f<i;f++){g=b[f];d=JSON.parse(e.getItem(g));d.key=g.replace(this.name+
|
||||
".","");c.push(d)}a&&this.fn(this.name,a).call(this,c);return this},remove:function(a,b){var c=this;if(this.isArray(a)){var d,g=a.length,f=function(i){c.remove(a[i],function(){--g>0||b&&c.lambda(b).call(c)})};for(d=0;d<a.length;d++)f(d);return this}d=this.name+"."+(a.key?a.key:a);this.indexer.del(d);e.removeItem(d);b&&this.lambda(b).call(this);return this},nuke:function(a){this.all(function(b){for(var c=0,d=b.length;c<d;c++)this.remove(b[c]);a&&this.lambda(a).call(this)});return this}}}());
|
19
src/manifest.json
Normal file
19
src/manifest.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Whiteout Mail",
|
||||
"description": "An offline enable mail app with integrated end-2-end messaging and a client-side encrypted message store both locally and in the cloud",
|
||||
"version": "0.0.1",
|
||||
"manifest_version": 2,
|
||||
"offline_enabled": true,
|
||||
"permissions": [
|
||||
"http://storage.whiteout.io/"
|
||||
],
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
}
|
||||
},
|
||||
"sandbox": {
|
||||
"pages": ["sandbox.html"],
|
||||
"content_security_policy": "sandbox allow-scripts; default-src 'self'; object-src 'none'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
}
|
||||
}
|
40
src/sandbox.html
Normal file
40
src/sandbox.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mail</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/jquery.mobile-1.2.0.min.css"/>
|
||||
<link rel="stylesheet" href="css/styles.css"/>
|
||||
|
||||
<!-- The Scripts -->
|
||||
<script src="lib/jquery-1.8.2.min.js"></script>
|
||||
<script src="lib/underscore-1.4.4.min.js"></script>
|
||||
<script src="lib/backbone-1.0.0.min.js"></script>
|
||||
<script src="js/jqm-config.js"></script>
|
||||
<script src="lib/jquery.mobile-1.2.0.min.js"></script>
|
||||
|
||||
<script src="lib/forge/forge.rsa.bundle.js"></script>
|
||||
<script src="lib/uuid.js"></script>
|
||||
|
||||
<script src="js/app-config.js"></script>
|
||||
|
||||
<script src="js/model/email-model.js"></script>
|
||||
<script src="js/model/folder-model.js"></script>
|
||||
<script src="js/model/account-model.js"></script>
|
||||
<script src="js/model/publickey-model.js"></script>
|
||||
|
||||
<script src="js/view/login-view.js"></script>
|
||||
<script src="js/view/compose-view.js"></script>
|
||||
<script src="js/view/messagelist-view.js"></script>
|
||||
<script src="js/view/messagelistitem-view.js"></script>
|
||||
<script src="js/view/folderlist-view.js"></script>
|
||||
<script src="js/view/read-view.js"></script>
|
||||
|
||||
<script src="js/app-router.js"></script>
|
||||
<script src="js/sandbox-loader.js"></script>
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
|
||||
</html>
|
@ -20,7 +20,7 @@
|
||||
<script src="../../src/lib/jquery-1.8.2.min.js"></script>
|
||||
<script src="../../src/lib/underscore-1.4.4.min.js"></script>
|
||||
<script src="../../src/lib/backbone-1.0.0.min.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-git.min.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-git.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-adapter-indexed-db-git.js"></script>
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
<script src="../../src/lib/jquery-1.8.2.min.js"></script>
|
||||
<script src="../../src/lib/underscore-1.4.4.min.js"></script>
|
||||
<script src="../../src/lib/backbone-1.0.0.min.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-git.min.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-git.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js"></script>
|
||||
<script src="../../src/lib/lawnchair/lawnchair-adapter-indexed-db-git.js"></script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user