start of sync refactor with tests
This commit is contained in:
Родитель
2c980a76f1
Коммит
2c63bd2286
|
@ -1,4 +1,6 @@
|
|||
<!doctype html>
|
||||
<script src="https://cdn.firebase.com/v0/firebase.js"></script>
|
||||
<script src='https://cdn.firebase.com/v0/firebase-auth-client.js'></script>
|
||||
<script src="lib/jquery.js"></script>
|
||||
<script src="lib/jsuri.js"></script>
|
||||
<script src="lib/tldjs.js"></script>
|
||||
|
@ -22,6 +24,8 @@
|
|||
<script src="captured_credential_storage.js"></script>
|
||||
<script src="site_configs.js"></script>
|
||||
<script src="gombot_sync.js"></script>
|
||||
<script src="sync_adapter.js"></script>
|
||||
<script src="firebase_sync.js"></script>
|
||||
<script src="models/login_credential.js"></script>
|
||||
<script src="collections/login_credential_collection.js"></script>
|
||||
<script src="models/user.js"></script>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
var FirebaseSync = function() {
|
||||
|
||||
var dataRef = new Firebase('https://gombot.firebaseIO.com');
|
||||
|
||||
var authClient = new FirebaseAuthClient(dataRef, authClientCallback);
|
||||
|
||||
var usersRef = dataRef.child('users');
|
||||
|
||||
var currentUser;
|
||||
|
||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||
function(details) {
|
||||
// Remove Origin header if it exists
|
||||
for (var i = 0; i < details.requestHeaders.length; ++i) {
|
||||
if (details.requestHeaders[i].name === 'Origin') {
|
||||
details.requestHeaders.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Remove Referer header if it exists
|
||||
for (var i = 0; i < details.requestHeaders.length; ++i) {
|
||||
if (details.requestHeaders[i].name === 'Referer') {
|
||||
details.requestHeaders.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// Add a Referer header for gombot.org
|
||||
details.requestHeaders.push({ name: 'Referer', value: 'https://gombot.org/'});
|
||||
return {requestHeaders: details.requestHeaders};
|
||||
},
|
||||
{ urls: ["https://auth.firebase.com/*"] },
|
||||
["blocking", "requestHeaders"]
|
||||
);
|
||||
|
||||
|
||||
function authClientCallback(error, user) {
|
||||
if (error) {
|
||||
// an error occurred while attempting login
|
||||
console.log(error);
|
||||
} else if (user) {
|
||||
currentUser = user;
|
||||
usersRef.child(user.id).on('value', getUserData);
|
||||
// user authenticated with Firebase
|
||||
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
|
||||
} else {
|
||||
if (currentUser) usersRef.child(currentUser.id).off('value', getUserData);
|
||||
currentUser = null;
|
||||
// user is logged out
|
||||
}
|
||||
}
|
||||
|
||||
function create(email, password, options) {
|
||||
authClient.createUser(email, password, function(error, user) {
|
||||
if (!error) {
|
||||
console.log('User Id: ' + user.id + ', Email: ' + user.email);
|
||||
} else {
|
||||
console.log("FirebaseSync.create error:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function login(email, password, options) {
|
||||
authClient.login('password', {
|
||||
email: email,
|
||||
password: password,
|
||||
rememberMe: true
|
||||
});
|
||||
}
|
||||
|
||||
function getUserData(data) {
|
||||
console.log("User data:", data.val());
|
||||
}
|
||||
|
||||
return {
|
||||
create: create,
|
||||
login: login,
|
||||
getUserData: getUserData
|
||||
};
|
||||
};
|
|
@ -45,9 +45,10 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
Gombot.TldService = getModule("TldService")(getModule("Tld"), getModule("Uri"));
|
||||
Gombot.SiteConfigs = getModule("SiteConfigs");
|
||||
Gombot.Realms = getModule("Realms")(Gombot, Gombot.SiteConfigs, getModule("Uri"));
|
||||
Gombot.Storage = getModule("Storage")(Backbone, _, Gombot.LocalStorage); // defined by backbone.localStorage.js
|
||||
Gombot.GombotClient = getModule("GombotClient");
|
||||
Gombot.Sync = getModule("GombotSync")(Gombot, Backbone, _);
|
||||
Gombot.Storage = getModule("Storage")(Backbone, _, Gombot.LocalStorage); // local sync; defined by backbone.localStorage.js
|
||||
//Gombot.GombotClient = getModule("GombotClient");
|
||||
//Gombot.Sync = getModule("GombotSync")(Gombot, Backbone, _); // original sync using our api
|
||||
//Gombot.FirebaseSync = getModule("FirebaseSync")(Gombot); // sync using firebase
|
||||
Gombot.LoginCredential = getModule("LoginCredential")(Gombot, Backbone, _);
|
||||
Gombot.LoginCredentialCollection = getModule("LoginCredentialCollection")(Backbone, _, Gombot.LoginCredential); // LoginCredential need to be initialized
|
||||
Gombot.CapturedCredentialStorage = getModule("CapturedCredentialStorage")(Gombot, getModule("Uri"));
|
||||
|
@ -55,8 +56,12 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
Gombot.AccountManager = getModule("AccountManager")(Gombot, _);
|
||||
Gombot.CommandHandler = getModule("CommandHandler")(Gombot, Gombot.Messaging, _);
|
||||
Gombot.Pages = getModule("Pages")(Gombot);
|
||||
Gombot.InfobarManager = getModule("InfobarManager");
|
||||
Gombot.Infobars = getModule("Infobars")(Gombot);
|
||||
Gombot.Crypto = getModule("GombotCrypto");
|
||||
Gombot.User = getModule("User")(Backbone, _, Gombot);
|
||||
if (typeof chrome !== "undefined") {
|
||||
Gombot.InfobarManager = getModule("InfobarManager");
|
||||
Gombot.Infobars = getModule("Infobars")(Gombot);
|
||||
}
|
||||
|
||||
var currentUser = null;
|
||||
Gombot.getCurrentUser = function() {
|
||||
|
@ -73,19 +78,24 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
currentUser.destroy({ localOnly: true, success: function() { currentUser = null; callback(); }});
|
||||
};
|
||||
|
||||
new Gombot.Storage("users", function(store) {
|
||||
Gombot.User = getModule("User")(Backbone, _, Gombot, store);
|
||||
Gombot.UserCollection = getModule("UserCollection")(Backbone, _, Gombot, store);
|
||||
checkFirstRun();
|
||||
});
|
||||
|
||||
function checkFirstRun() {
|
||||
Gombot.LocalStorage.getItem("firstRun", function(firstRun) {
|
||||
initGombot(firstRun);
|
||||
Gombot.init = function(options) {
|
||||
options = options || {};
|
||||
options.storeName = options.storeName || "users";
|
||||
options.callback = options.callback || checkFirstRun;
|
||||
new Gombot.Storage(options.storeName, function(store) {
|
||||
Gombot.SyncAdapter = getModule("SyncAdapter")(Gombot, Gombot.Crypto, store, _);
|
||||
Gombot.UserCollection = getModule("UserCollection")(Backbone, _, Gombot, store);
|
||||
options.callback();
|
||||
});
|
||||
}
|
||||
|
||||
function initGombot(firstRun) {
|
||||
function checkFirstRun() {
|
||||
Gombot.LocalStorage.getItem("firstRun", function(firstRun) {
|
||||
fetchUsers(firstRun);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchUsers(firstRun) {
|
||||
Gombot.users = new Gombot.UserCollection();
|
||||
Gombot.users.fetch({
|
||||
success: function() {
|
||||
|
@ -102,6 +112,7 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = _Gombot; // export namespace constructor, for Firefox
|
||||
} else { // otherwise, just create the global Gombot namespace
|
||||
} else { // otherwise, just create the global Gombot namespace and init
|
||||
var Gombot = _Gombot({});
|
||||
Gombot.init();
|
||||
}
|
||||
|
|
|
@ -47,33 +47,9 @@ windows.on('open', function(window) {
|
|||
addToolbarButton();
|
||||
});
|
||||
|
||||
/** Load all Gombot modules **/
|
||||
|
||||
var gombotModules = {
|
||||
Backbone: require("./lib/backbone"),
|
||||
_ : require("./lib/underscore"),
|
||||
Messaging: require("./messaging"),
|
||||
LocalStorage: require("./local_storage"),
|
||||
Tld: require("./lib/tld.js"),
|
||||
Uri: require("./lib/jsuri"),
|
||||
TldService: require("./tld_service"),
|
||||
SiteConfigs: require("./site_configs"),
|
||||
Realms: require("./realms"),
|
||||
Storage: require("./storage"),
|
||||
GombotClient: require("./client/client"),
|
||||
GombotSync: require("./gombot_sync"),
|
||||
LoginCredential: require("./models/login_credential"),
|
||||
LoginCredentialCollection: require("./collections/login_credential_collection"),
|
||||
CapturedCredentialStorage: require("./captured_credential_storage"),
|
||||
Linker: require("./linker"),
|
||||
CommandHandler: require("./command_handler"),
|
||||
User: require("./models/user"),
|
||||
UserCollection: require("./collections/user_collection"),
|
||||
AccountManager: require("./account_manager"),
|
||||
Pages: require("./pages")
|
||||
};
|
||||
|
||||
var Gombot = require("./gombot")(gombotModules);
|
||||
var GombotModules = require("./modules");
|
||||
var Gombot = require("./gombot")(GombotModules);
|
||||
Gombot.init();
|
||||
|
||||
/** Tpp panel stuff **/
|
||||
|
||||
|
@ -109,3 +85,5 @@ pageMod.PageMod({
|
|||
Gombot.Messaging.registerPageModWorker(worker);
|
||||
}
|
||||
});
|
||||
|
||||
exports.gombot = Gombot;
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
var User = function(Backbone, _, Gombot, LocalStorage) {
|
||||
var User = function(Backbone, _, Gombot) {
|
||||
|
||||
const USER_DATA_VERSIONS = [
|
||||
"identity.mozilla.com/gombot/v1/userData"
|
||||
];
|
||||
|
||||
var GombotSync = Gombot.Sync,
|
||||
LoginCredentialCollection = Gombot.LoginCredentialCollection;
|
||||
|
||||
// attributes should be something like:
|
||||
// {
|
||||
// "version": "identity.mozilla.com/gombot/v1/userData",
|
||||
|
@ -38,8 +35,6 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
|||
disabledSites: {}
|
||||
},
|
||||
|
||||
localStorage: LocalStorage,
|
||||
|
||||
initialize: function() {
|
||||
Backbone.Model.prototype.initialize.apply(this, arguments);
|
||||
this.addSyncListener(this.get("logins"));
|
||||
|
@ -64,6 +59,7 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
|||
},
|
||||
|
||||
isAuthenticated: function() {
|
||||
return false;
|
||||
return this.client && ((this.client.isAuthenticated && this.client.isAuthenticated()) || (this.client.keys && this.client.user));
|
||||
},
|
||||
|
||||
|
@ -71,60 +67,61 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
|||
// call model.toJSON({ encrypted: true, ciphertext: <ciphertext> })
|
||||
// Other toJSON() creates a standard plaintext representation of a User object
|
||||
toJSON: function(args) {
|
||||
var result;
|
||||
args = args || {};
|
||||
if (args.ciphertext) {
|
||||
result = { ciphertext: args.ciphertext, updated: this.updated, id: this.id, email: this.get("email"), version: this.get("version") };
|
||||
if (this.isAuthenticated()) _.extend(result, { client: this.client.toJSON() });
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
result = Backbone.Model.prototype.toJSON.apply(this, arguments);
|
||||
return _.extend(result, { logins: this.get("logins").toJSON() });
|
||||
var result = Backbone.Model.prototype.toJSON.apply(this, arguments);
|
||||
return _.extend(result, { logins: this.get("logins").toJSON() });
|
||||
},
|
||||
|
||||
// Returns an object containing key/values of data that will be
|
||||
// stored in plaintext with an encrypted copy of this model's data.
|
||||
// The metadata should not contain any information that is intended
|
||||
// to be stored encrypted at rest.
|
||||
getMetadata: function() {
|
||||
return {
|
||||
id: this.id,
|
||||
email: this.get("email"),
|
||||
version: this.get("version"),
|
||||
updated: this.updated
|
||||
}
|
||||
},
|
||||
|
||||
parse: function(resp) {
|
||||
if (resp.ciphertext) this.ciphertext = resp.ciphertext;
|
||||
if (resp.updated) this.updated = resp.updated;
|
||||
if (resp.client) this.client = resp.client;
|
||||
delete resp.ciphertext;
|
||||
delete resp.updated;
|
||||
delete resp.client;
|
||||
return resp;
|
||||
},
|
||||
|
||||
sync: function(method, model, options) {
|
||||
var self = this;
|
||||
var success = function(resp) {
|
||||
var s = options.success;
|
||||
options.success = function(model, resp, options) {
|
||||
console.log("User.sync finished method="+method+" resp="+JSON.stringify(resp)+" model="+JSON.stringify(model));
|
||||
// resp.data is returned by GombotSync calls with plaintext user data
|
||||
if (s) s(model, resp.data || {}, options);
|
||||
}
|
||||
if (resp.updated) self.updated = resp.updated;
|
||||
// ciphertext in resp indicates we need to write it out to local storage
|
||||
if (resp.ciphertext) {
|
||||
if (method === "read") {
|
||||
self.save(resp.data, _.extend(options, { localOnly: true, ciphertext: resp.ciphertext }));
|
||||
} else {
|
||||
console.log("localSync method="+method);
|
||||
Backbone.localSync(method, model, _.extend(options, { ciphertext: resp.ciphertext }));
|
||||
}
|
||||
} else if (options.success) {
|
||||
options.success(model, resp, options);
|
||||
}
|
||||
};
|
||||
var error = function(args) {
|
||||
if (options.error) options.error(args);
|
||||
};
|
||||
var o = _.clone(options);
|
||||
if (options.localOnly) {
|
||||
Backbone.localSync(method, model, options);
|
||||
} else {
|
||||
GombotSync.sync(method, model, _.extend(o,{ success: success, error: error }));
|
||||
}
|
||||
Gombot.SyncAdapter.sync(method, model, options);
|
||||
// var self = this;
|
||||
// var success = function(resp) {
|
||||
// var s = options.success;
|
||||
// options.success = function(model, resp, options) {
|
||||
// console.log("User.sync finished method="+method+" resp="+JSON.stringify(resp)+" model="+JSON.stringify(model));
|
||||
// // resp.data is returned by GombotSync calls with plaintext user data
|
||||
// if (s) s(model, resp.data || {}, options);
|
||||
// }
|
||||
// if (resp.updated) self.updated = resp.updated;
|
||||
// // ciphertext in resp indicates we need to write it out to local storage
|
||||
// if (resp.ciphertext) {
|
||||
// if (method === "read") {
|
||||
// self.save(resp.data, _.extend(options, { localOnly: true, ciphertext: resp.ciphertext }));
|
||||
// } else {
|
||||
// console.log("localSync method="+method);
|
||||
// Backbone.localSync(method, model, _.extend(options, { ciphertext: resp.ciphertext }));
|
||||
// }
|
||||
// } else if (options.success) {
|
||||
// options.success(model, resp, options);
|
||||
// }
|
||||
// };
|
||||
// var error = function(args) {
|
||||
// if (options.error) options.error(args);
|
||||
// };
|
||||
// var o = _.clone(options);
|
||||
// if (options.localOnly) {
|
||||
// Backbone.localSync(method, model, options);
|
||||
// } else {
|
||||
// GombotSync.sync(method, model, _.extend(o,{ success: success, error: error }));
|
||||
// }
|
||||
},
|
||||
|
||||
set: function(key, val, options) {
|
||||
|
@ -137,9 +134,9 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
|||
} else {
|
||||
(attributes = {})[key] = val;
|
||||
}
|
||||
if (attributes.logins !== undefined && !(attributes.logins instanceof LoginCredentialCollection)) {
|
||||
if (attributes.logins !== undefined && !(attributes.logins instanceof Gombot.LoginCredentialCollection)) {
|
||||
logins = attributes.logins;
|
||||
attributes.logins = this.get("logins") || new LoginCredentialCollection();
|
||||
attributes.logins = this.get("logins") || new Gombot.LoginCredentialCollection();
|
||||
}
|
||||
result = Backbone.Model.prototype.set.call(this, attributes, options);
|
||||
if (result && logins) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/** Load all Gombot modules **/
|
||||
|
||||
var GombotModules = {
|
||||
Backbone: require("./lib/backbone"),
|
||||
_ : require("./lib/underscore"),
|
||||
Messaging: require("./messaging"),
|
||||
LocalStorage: require("./local_storage"),
|
||||
Tld: require("./lib/tld.js"),
|
||||
Uri: require("./lib/jsuri"),
|
||||
TldService: require("./tld_service"),
|
||||
SiteConfigs: require("./site_configs"),
|
||||
Realms: require("./realms"),
|
||||
Storage: require("./storage"),
|
||||
//GombotClient: require("./client/client"),
|
||||
//GombotSync: require("./gombot_sync"),
|
||||
LoginCredential: require("./models/login_credential"),
|
||||
LoginCredentialCollection: require("./collections/login_credential_collection"),
|
||||
CapturedCredentialStorage: require("./captured_credential_storage"),
|
||||
Linker: require("./linker"),
|
||||
CommandHandler: require("./command_handler"),
|
||||
User: require("./models/user"),
|
||||
UserCollection: require("./collections/user_collection"),
|
||||
AccountManager: require("./account_manager"),
|
||||
Pages: require("./pages"),
|
||||
GombotCrypto: require("./client/crypto"),
|
||||
SyncAdapter: require("./sync_adapter")
|
||||
};
|
||||
|
||||
module.exports = GombotModules;
|
|
@ -0,0 +1,121 @@
|
|||
var SyncAdapter = function(Gombot, GombotCrypto, SyncStrategy, _) {
|
||||
|
||||
// TODO: seed from an actual entropy source
|
||||
GombotCrypto.seed("oiqwjeciouqh3c89cnkjasdcnasjf84u9jcuqwiench734fhujhwuqhf73f73fhsdjfhasdf734fhdkcnuf"+(new Date().toString()) ,function(err) {
|
||||
if (err) console.log("GombotCrypto.seed error:", err);
|
||||
});
|
||||
|
||||
function maybeHandleError(handler, err) {
|
||||
if (err) {
|
||||
console.log("SyncMediator error", err);
|
||||
if (handler) handler(err);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
function encryptModel(model, keys, options) {
|
||||
GombotCrypto.encrypt(keys, JSON.stringify(model), function(err, ciphertext) {
|
||||
if (err) return maybeHandleError(options.error, err);
|
||||
options.success(ciphertext);
|
||||
});
|
||||
}
|
||||
|
||||
function decryptModelData(ciphertext, keys, options) {
|
||||
GombotCrypto.decrypt(keys, ciphertext, function(err, json) {
|
||||
var modelData;
|
||||
try {
|
||||
if (!err) {
|
||||
modelData = JSON.parse(json);
|
||||
}
|
||||
} catch (e) {
|
||||
err = new Error("Could not parse decrypted JSON:", json);
|
||||
}
|
||||
if (err) return maybeHandleError(options.error, err);
|
||||
options.success(modelData);
|
||||
});
|
||||
}
|
||||
|
||||
function createCryptoProxyForModel(model, keys) {
|
||||
var clone = _.clone(model);
|
||||
return _.extend(clone, {
|
||||
toJSON: function(options) {
|
||||
// missing options means synchronous response to underyling object
|
||||
if (!options || !options.success) return model.toJSON();
|
||||
var o = _.clone(options);
|
||||
encryptModel(model, keys, _.extend(o, { success: function(ciphertext) {
|
||||
options.success(_.extend(model.getMetadata(), {
|
||||
ciphertext: ciphertext // encrypted plaintext model
|
||||
}));
|
||||
}}));
|
||||
},
|
||||
parse: function(resp, options) {
|
||||
var o = _.clone(options),
|
||||
ciphertext = resp.ciphertext;
|
||||
decryptModelData(ciphertext, keys, _.extend(o, { success: function(modelData) {
|
||||
delete resp.ciphertext;
|
||||
options.success(_.extend(resp, modelData));
|
||||
}}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getCryptoProxyForModel(model, options) {
|
||||
if (model.cryptoProxy) return options.success(model.cryptoProxy);
|
||||
var o = _.clone(options);
|
||||
deriveKeysForModel(model, _.extend(o, { success: function(keys) {
|
||||
model.cryptoProxy = createCryptoProxyForModel(model, keys);
|
||||
options.success(model.cryptoProxy);
|
||||
}}));
|
||||
}
|
||||
|
||||
var kdf = GombotCrypto.derive;
|
||||
// Special kdf derivation function we'll pass to GombotClient to handle FX slowness bug
|
||||
if (typeof require !== "undefined") {
|
||||
kdf = function (args, callback) {
|
||||
console.log("in derive")
|
||||
require("gombot-crypto-jetpack").kdf(args.email, args.password).then(function(keys) {
|
||||
callback(null, keys);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// options.password must be present
|
||||
function deriveKeysForModel(model, options) {
|
||||
kdf({
|
||||
email: model.get("email"),
|
||||
password: options.password
|
||||
}, function(err, keys) {
|
||||
if (err) return maybeHandleError(options.error, err);
|
||||
options.success(keys);
|
||||
});
|
||||
}
|
||||
|
||||
function setSyncStrategy(strategy) {
|
||||
SyncStrategy = strategy;
|
||||
}
|
||||
|
||||
function sync(method, model, options) {
|
||||
if (!(model instanceof Gombot.User)) {
|
||||
if (options.error) options.error("sync only supports syncing instances of Gombot.User");
|
||||
return false;
|
||||
}
|
||||
var o = _.clone(options);
|
||||
getCryptoProxyForModel(model, _.extend(o, { success: function(cryptoProxyForModel) {
|
||||
var o = _.clone(options);
|
||||
// translate model proxy back to original model
|
||||
SyncStrategy.sync(method, cryptoProxyForModel, _.extend(o, { success: function(modelProxy, resp, modifiedOptions) {
|
||||
if (options.success) options.success(model, resp, options);
|
||||
}}));
|
||||
}}));
|
||||
}
|
||||
|
||||
return {
|
||||
sync: sync,
|
||||
setSyncStrategy: setSyncStrategy
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = SyncAdapter;
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* https://github.com/jeromegn/Backbone.localStorage
|
||||
*/
|
||||
var Storage = function(Backbone, _, LocalStorage) {
|
||||
var Storage = function(Backbone, _, LocalStorage, store) {
|
||||
return (function (root, factory) {
|
||||
// if (typeof define === "function" && define.amd) {
|
||||
// // AMD. Register as an anonymous module.
|
||||
|
@ -62,9 +62,12 @@ _.extend(Backbone.LocalStorage.prototype, {
|
|||
var cb = _.after(2, function() {
|
||||
callback(model.toJSON());
|
||||
});
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model.toJSON({ ciphertext: options.ciphertext })), cb);
|
||||
this.records.push(model.id.toString());
|
||||
this.save(cb);
|
||||
var o = _.clone(options);
|
||||
model.toJSON(_.extend(o, { success: (function(jsonObj) {
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(jsonObj), cb);
|
||||
this.records.push(model.id.toString());
|
||||
this.save(cb);
|
||||
}).bind(this)}));
|
||||
return;
|
||||
},
|
||||
|
||||
|
@ -116,6 +119,12 @@ _.extend(Backbone.LocalStorage.prototype, {
|
|||
// fix for "illegal access" error on Android when JSON.parse is passed null
|
||||
jsonData: function (data) {
|
||||
return data && JSON.parse(data);
|
||||
},
|
||||
|
||||
sync: function(method, model, options) {
|
||||
var o = _.clone(options);
|
||||
o.store = this;
|
||||
Backbone.LocalStorage.sync(method, model, o);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -124,7 +133,7 @@ _.extend(Backbone.LocalStorage.prototype, {
|
|||
// *localStorage* property, which should be an instance of `Store`.
|
||||
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
|
||||
Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, options) {
|
||||
var store = model.localStorage || model.collection.localStorage;
|
||||
var store = options.store || model.localStorage || model.collection.localStorage;
|
||||
|
||||
var syncDfd = (typeof $ !== "undefined") && $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
|
||||
|
||||
|
@ -149,6 +158,8 @@ Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, option
|
|||
if (options && options.complete) options.complete(resp);
|
||||
};
|
||||
|
||||
console.log("Backbone.LocalStorage.sync", method, model, options);
|
||||
|
||||
switch (method) {
|
||||
case "read": model.id != undefined ? store.find(model, callback, options) : store.findAll(callback, options); break;
|
||||
case "create": store.create(model, callback, options); break;
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
"notifications",
|
||||
"storage",
|
||||
"http://*/",
|
||||
"https://*/"
|
||||
"https://*/",
|
||||
"webRequest",
|
||||
"webRequestBlocking"
|
||||
],
|
||||
"icons": {
|
||||
"128": "images/gombot-icon-128.png"
|
||||
},
|
||||
|
||||
"content_security_policy": "script-src 'self' https://cdn.firebase.com https://auth.firebase.com https://*.firebaseio.com; object-src 'self'",
|
||||
"background": {
|
||||
"page": "background/background.html"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
exports.testCreate = function(test) {
|
||||
var GombotModules = require("./modules");
|
||||
var Gombot = require("./gombot")(GombotModules);
|
||||
|
||||
Gombot.init({ storeName: "testUsers", callback: function() {
|
||||
var email = "test+"+Math.floor((1+Math.random())*10000)+"@test.com";
|
||||
var u = new Gombot.User({ email: email });
|
||||
u.save(null, { success: function() {
|
||||
console.log("user saved", u);
|
||||
test.pass();
|
||||
test.done();
|
||||
},
|
||||
error: function(err) {
|
||||
console.log("error:", err);
|
||||
test.fail();
|
||||
test.done();
|
||||
},
|
||||
password: "foobar"
|
||||
});
|
||||
}});
|
||||
test.waitUntilDone();
|
||||
}
|
Загрузка…
Ссылка в новой задаче