still totally broken
This commit is contained in:
Родитель
d5993a17ca
Коммит
2db5f5515d
|
@ -2,8 +2,9 @@
|
|||
<script src="../lib/jquery.js"></script>
|
||||
<script src="../lib/jsuri.js"></script>
|
||||
<script src="../lib/tldjs.js"></script>
|
||||
<script src="../lib/backbone-min.js"></script>
|
||||
<script src="../lib/underscore.js"></script>
|
||||
<script src="../lib/backbone-min.js"></script>
|
||||
<script src="../lib/backbone.localStorage.js"></script>
|
||||
<script src="../infobar/manager.js"></script>
|
||||
<script src="util.js"></script>
|
||||
<script src="tld_service.js"></script>
|
||||
|
@ -14,11 +15,13 @@
|
|||
<script src="first_run.js"></script>
|
||||
<script src="chrome_messaging.js"></script>
|
||||
<script src="command_handler.js"></script>
|
||||
<script src="storage.js"></script>
|
||||
<script src="local_storage.js"></script>
|
||||
<script src="captured_credential_storage.js"></script>
|
||||
<script src="site_configs.js"></script>
|
||||
<script src="linked_site.js"></script>
|
||||
<script src="linked_site_collection.js"></script>
|
||||
<script src="user.js"></script>
|
||||
<script src="user_collection.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<body>
|
||||
<!-- Invisible <textarea> used to copy data onto the clipboard -->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var LinkedSite = function(Backbone) {
|
||||
var LinkedSite = function(Backbone, _) {
|
||||
|
||||
// LinkedSite constructor
|
||||
// data is:
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
var LinkedSiteCollection = function(Backbone, LinkedSite) {
|
||||
var LinkedSiteCollection = function(Backbone, _, LinkedSite) {
|
||||
|
||||
var LinkedSiteCollection = Backbone.Collection.extend({
|
||||
model: LinkedSite,
|
||||
initializeFromRealmLoginMap: function(logins) {
|
||||
var realms = _.keys(logins);
|
||||
realms.forEach((function(realm) {
|
||||
this.add(linkedSites[realm]);
|
||||
}).bind(this));
|
||||
});
|
||||
|
||||
parse: function(resp) {
|
||||
return _.flatten(_.values(resp), true);
|
||||
},
|
||||
|
||||
toJSON: function(options) {
|
||||
var result = {};
|
||||
this.each(function(model) {
|
||||
var realm = model.get("realm");
|
||||
if (!result[realm]) {
|
||||
result[realm] = [];
|
||||
}
|
||||
result[realm].push(model.toJSON(options));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return LinkedSiteCollection;
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* storage.js
|
||||
*
|
||||
*
|
||||
* Handles persisting user data via localStorage.
|
||||
*
|
||||
*/
|
||||
|
||||
// This handles the low-level localStorage
|
||||
// TODO: handle errors
|
||||
var LocalStorage = function() {
|
||||
return {
|
||||
getItem: function(key, callback) {
|
||||
chrome.storage.local.get(key, function(storageObj) {
|
||||
callback(storageObj[key]);
|
||||
});
|
||||
},
|
||||
setItem: function(key, data, callback) {
|
||||
var updatedObj = {};
|
||||
updatedObj[key] = data;
|
||||
chrome.storage.local.set(updatedObj, callback);
|
||||
},
|
||||
removeItem: function(key, callback) {
|
||||
chrome.storage.local.remove(key, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// var User = (function(Storage){
|
||||
|
||||
// // In-memory storage for user data that gets synced.
|
||||
// var _logins = {};
|
||||
// var _neverAsk = {};
|
||||
// var _pin = null;
|
||||
|
||||
// // In-memory storasge for user data that's persisted to localStorage, but not synced.
|
||||
// var _didFirstRun = false;
|
||||
|
||||
// // The key for the document in localStorage which holds all of the user
|
||||
// // data synced with the server.
|
||||
// const SYNC_DOCUMENT_KEY = 'gombot_user_data';
|
||||
|
||||
// const DATA_VERSION = 'identity.mozilla.com/gombot/v1/userData';
|
||||
|
||||
// // NB: remove every call to fetchStoredData and updateStoredData before commit!
|
||||
|
||||
// function fetchStoredData(callback) {
|
||||
// var keysToFetch = [
|
||||
// SYNC_DOCUMENT_KEY,
|
||||
// 'did_first_run'
|
||||
// ];
|
||||
// chrome.storage.local.get(SYNC_DOCUMENT_KEY, function(storageObj) {
|
||||
// var userData = storageObj;//[SYNC_DOCUMENT_KEY];
|
||||
// if (userData === undefined) {
|
||||
// userData = {
|
||||
// version: DATA_VERSION,
|
||||
// logins: {},
|
||||
// pin: loginsLock.pin || null,
|
||||
// neverAsk: {}
|
||||
// };
|
||||
// }
|
||||
// callback(userData);
|
||||
// });
|
||||
// }
|
||||
|
||||
// // function updateStoredData(obj) {
|
||||
// // var updatedObj = {};
|
||||
// // updatedObj[SYNC_DOCUMENT_KEY] = obj;
|
||||
// // chrome.storage.local.set(updatedObj);
|
||||
// // }
|
||||
|
||||
// function saveToLocalStorage() {
|
||||
// Storage.set(SYNC_DOCUMENT_KEY, {
|
||||
// version: DATA_VERSION,
|
||||
// logins: _logins || {},
|
||||
// pin: _pin || null,
|
||||
// neverAsk: _neverAsk || {}
|
||||
// });
|
||||
// Storage.set('did_first_run',_didFirstRun);
|
||||
// }
|
||||
|
||||
// function loadFromLocalStorage() {
|
||||
// fetchStoredData(function(userData) {
|
||||
// _logins = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
// _pin = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
// _neverAsk = userData[SYNC_DOCUMENT_KEY].neverAsk;
|
||||
// _didFirstRun = userData['did_first_run'];
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Load from localStorage into memory when extension starts.
|
||||
// loadFromLocalStorage();
|
||||
|
||||
// var userData = {};
|
||||
|
||||
// var loginsObj = (function() {
|
||||
// function formatStoredLogin(login) {
|
||||
// return {
|
||||
// username: login.username,
|
||||
// password: login.password,
|
||||
// hostname: login.hostname,
|
||||
|
||||
// // Fields that may be missing
|
||||
// title: login['title'] || '',
|
||||
// url: login['url'] || '',
|
||||
// pinLocked: login['pinLocked'] || false,
|
||||
// supplementalInformation: login['supplementalInformation'] || {}
|
||||
// };
|
||||
// }
|
||||
// return {
|
||||
// // Save a new login object to localStorage
|
||||
// add: function(newLogin) {
|
||||
// var loginObj = formatStoredLogin(newLogin);
|
||||
// // Filter for logins with the same username and hostname.
|
||||
// var existingLoginsForHost = _logins[newLogin.hostname] || [];
|
||||
// _logins[newLogin.hostname] =
|
||||
// existingLoginsForHost.filter(function(_login) {
|
||||
// return _login.username != loginObj.username;
|
||||
// });
|
||||
// _logins[newLogin.hostname].push(loginObj);
|
||||
// saveToLocalStorage();
|
||||
// },
|
||||
// // Takes a hostname and a callback, and passes it a list of login
|
||||
// // objects the user has saved for that domain.
|
||||
// getForHostname: function(hostname/*,callback*/) {
|
||||
// return (_logins[hostname] || []);
|
||||
// },
|
||||
|
||||
// // Mainly for debugging purposes.
|
||||
// deleteForHostname: function(hostname) {
|
||||
// delete _logins[hostname];
|
||||
// saveToLocalStorage();
|
||||
// }
|
||||
// };
|
||||
// })();
|
||||
|
||||
// var neverAskObj = (function() {
|
||||
// return {
|
||||
// // Add a hostname to the list of sites for which we never
|
||||
// // prompt to save passwords
|
||||
// add: function(siteHostname) {
|
||||
// if (!(siteHostname in _neverAsk)) {
|
||||
// _neverAsk[siteHostname] = 'all';
|
||||
// saveToLocalStorage();
|
||||
// }
|
||||
// },
|
||||
// // Takes a callback, and passes it a list of domains the user
|
||||
// // has elected to never save logins on.
|
||||
// get: function(/*callback*/) {
|
||||
// return _.keys(userData.neverAsk);
|
||||
// },
|
||||
// // Takes a hostname and a callback. Passes callback a boolean,
|
||||
// // indicating if the user *should* be asked about logins
|
||||
// // on that domain.
|
||||
// checkForHostname: function(hostname/*,callback*/) {
|
||||
// return !(hostname in this.get());
|
||||
// }
|
||||
// };
|
||||
// })();
|
||||
|
||||
// var firstRunObj = (function(){
|
||||
// return {
|
||||
// wasCompleted: function() {
|
||||
// // Takes a callback, and passes it a boolean indicating whether
|
||||
// // or not the user has completed the first run flow.
|
||||
// // Note that this will return true if the user started the first
|
||||
// // run flow but did not complete it.
|
||||
// return _didFirstRun;
|
||||
// },
|
||||
// setIfCompleted: function(firstRunFinished) {
|
||||
// //setIfDidFirstRun
|
||||
// // Set (or unset) the flag indicating the user has finished the first
|
||||
// // run flow.
|
||||
// _didFirstRun = firstRunFinished;
|
||||
// saveToLocalStorage();
|
||||
// }
|
||||
// };
|
||||
// })();
|
||||
|
||||
// var pinObj = (function() {
|
||||
// return {
|
||||
// validate: function(testPin) {
|
||||
// return testPin == _pin;
|
||||
// },
|
||||
// get: function() {
|
||||
// return _pin;
|
||||
// },
|
||||
// set: function(newPIN) {
|
||||
// _pin = newPIN || null;
|
||||
// saveToLocalStorage();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// return {
|
||||
// Logins: loginsObj,
|
||||
// neverAsk: neverAskObj,
|
||||
// firstRun: firstRunObj,
|
||||
// PIN: pinObj
|
||||
// };
|
||||
// })(Gombot.Storage);
|
||||
|
||||
// // Takes a callback, and passes it a list of domains the user
|
||||
// // has elected to never save logins on.
|
||||
// function getNeverSaveOnSites(callback) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// callback(_.keys(userData.neverAsk));
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Takes a hostname and a callback, and passes it a list of login
|
||||
// // objects the user has saved for that domain.
|
||||
// function getLoginsForSite(hostname,callback) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// callback(userData.logins[hostname] || []);
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Mainly for debugging purposes.
|
||||
// function deleteLoginsForSite(hostname) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// delete userData[hostname];
|
||||
// updateStoredData(userData);
|
||||
// });
|
||||
// }
|
||||
|
||||
// Returns a string of a comma separated value file containing the hostname, username,
|
||||
// and password for each login the user has saved.
|
||||
function getLoginsCSV(callback) {
|
||||
// Add header
|
||||
var retVal = "hostname,username,password\n";
|
||||
fetchStoredData(function(userData) {
|
||||
for (var item in _.keys(userData.logins)) {
|
||||
for (var login in userData.logins[item]) {
|
||||
retVal += login.hostname + ',' + login.username
|
||||
+ ',' + login.password + '\n';
|
||||
}
|
||||
}
|
||||
callback(retVal);
|
||||
});
|
||||
}
|
||||
|
||||
// Dump localStorage to CSV file, for debugging purposes.
|
||||
function downloadExportDataFile() {
|
||||
// Get entire content of localStorage
|
||||
// NB: This contains all of the user's passwords in plaintext, as well as
|
||||
// their PIN and not-so-useful flags like did_first_run.
|
||||
getLoginsCSV(function(loginsCSV) {
|
||||
// Turn storageObj into a blob
|
||||
var blob = new window.Blob([loginsCSV], {type: 'text/csv'});
|
||||
|
||||
// Creates a link that opens the blob on the background page,
|
||||
// and then clicks it. Cribbed from:
|
||||
// http://stackoverflow.com/questions/4845215/making-a-chrome-extension-download-a-file
|
||||
var a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = 'passwords_dump.csv';
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
delete a;// we don't need this anymore
|
||||
});
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
initGombot();
|
||||
//initGombot();
|
||||
|
||||
var Gombot = {};
|
||||
Gombot.Messaging = ChromeMessaging();
|
||||
|
@ -16,18 +16,38 @@ Gombot.Realms = Realms(Gombot.SiteConfigs, Gombot.TldService);
|
|||
Gombot.CapturedCredentialStorage = CapturedCredentialStorage(Gombot.Realms);
|
||||
Gombot.CommandHandler = CommandHandler(Gombot.Messaging, Gombot.CapturedCredentialStorage, Gombot.Realms);
|
||||
|
||||
Gombot.Storage = Storage();
|
||||
Gombot.User = User(Gombot.Storage);
|
||||
Gombot.LocalStorage = LocalStorage();
|
||||
Gombot.Storage = Storage(Backbone, _, Gombot.LocalStorage);
|
||||
|
||||
Gombot.LinkedSite = LinkedSite(Backbone, _);
|
||||
Gombot.LinkedSiteCollection = LinkedSiteCollection(Backbone, _, Gombot.LinkedSite);
|
||||
Gombot.User = User(Backbone, _, Gombot.LinkedSiteCollection);
|
||||
|
||||
//Gombot.LocalStorage = LocalSync(Backbone, _, Gombot.Storage);
|
||||
|
||||
|
||||
var usersStore = new Gombot.Storage("users", function() {
|
||||
Gombot.UserCollection = UserCollection(Backbone, _, Gombot.User, usersStore);
|
||||
initGombot();
|
||||
});
|
||||
|
||||
|
||||
// var Users = new Gombot.UserCollection();
|
||||
// var currentUser;
|
||||
|
||||
function initGombot() {
|
||||
var users = new Gombot.UserCollection();
|
||||
users.fetch();
|
||||
// Users.fetch({ success: function(collection, response, options) {
|
||||
// currentUser = collection
|
||||
// }});
|
||||
// Load blacklisted sites from localStorage
|
||||
// loadNeverSaveOnSites();
|
||||
// Load PIN lock state from localStorage
|
||||
// loadLoginsLock();
|
||||
if (!User.firstRun.wasCompleted()) {
|
||||
startFirstRunFlow();
|
||||
}
|
||||
// if (!User.firstRun.wasCompleted()) {
|
||||
// startFirstRunFlow();
|
||||
// }
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1,259 +0,0 @@
|
|||
/*
|
||||
* storage.js
|
||||
*
|
||||
*
|
||||
* Handles persisting user data via localStorage.
|
||||
*
|
||||
*/
|
||||
|
||||
// This handles the low-level localStorage
|
||||
var Storage = function() {
|
||||
return {
|
||||
get: function(key, callback) {
|
||||
chrome.storage.local.get(key, function(storageObj) {
|
||||
callback(storageObj[key]);
|
||||
});
|
||||
},
|
||||
set: function(key, data, callback) {
|
||||
var updatedObj = {};
|
||||
updatedObj[key] = data;
|
||||
chrome.storage.local.set(updatedObj, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var User = (function(Storage){
|
||||
|
||||
// In-memory storage for user data that gets synced.
|
||||
var _logins = {};
|
||||
var _neverAsk = {};
|
||||
var _pin = null;
|
||||
|
||||
// In-memory storasge for user data that's persisted to localStorage, but not synced.
|
||||
var _didFirstRun = false;
|
||||
|
||||
// The key for the document in localStorage which holds all of the user
|
||||
// data synced with the server.
|
||||
const SYNC_DOCUMENT_KEY = 'gombot_user_data';
|
||||
|
||||
const DATA_VERSION = 'identity.mozilla.com/gombot/v1/userData';
|
||||
|
||||
// NB: remove every call to fetchStoredData and updateStoredData before commit!
|
||||
|
||||
function fetchStoredData(callback) {
|
||||
var keysToFetch = [
|
||||
SYNC_DOCUMENT_KEY,
|
||||
'did_first_run'
|
||||
];
|
||||
chrome.storage.local.get(SYNC_DOCUMENT_KEY, function(storageObj) {
|
||||
var userData = storageObj;//[SYNC_DOCUMENT_KEY];
|
||||
if (userData === undefined) {
|
||||
userData = {
|
||||
version: DATA_VERSION,
|
||||
logins: {},
|
||||
pin: loginsLock.pin || null,
|
||||
neverAsk: {}
|
||||
};
|
||||
}
|
||||
callback(userData);
|
||||
});
|
||||
}
|
||||
|
||||
// function updateStoredData(obj) {
|
||||
// var updatedObj = {};
|
||||
// updatedObj[SYNC_DOCUMENT_KEY] = obj;
|
||||
// chrome.storage.local.set(updatedObj);
|
||||
// }
|
||||
|
||||
function saveToLocalStorage() {
|
||||
Storage.set(SYNC_DOCUMENT_KEY, {
|
||||
version: DATA_VERSION,
|
||||
logins: _logins || {},
|
||||
pin: _pin || null,
|
||||
neverAsk: _neverAsk || {}
|
||||
});
|
||||
Storage.set('did_first_run',_didFirstRun);
|
||||
}
|
||||
|
||||
function loadFromLocalStorage() {
|
||||
fetchStoredData(function(userData) {
|
||||
_logins = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_pin = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_neverAsk = userData[SYNC_DOCUMENT_KEY].neverAsk;
|
||||
_didFirstRun = userData['did_first_run'];
|
||||
});
|
||||
}
|
||||
|
||||
// Load from localStorage into memory when extension starts.
|
||||
loadFromLocalStorage();
|
||||
|
||||
var userData = {};
|
||||
|
||||
var loginsObj = (function() {
|
||||
function formatStoredLogin(login) {
|
||||
return {
|
||||
username: login.username,
|
||||
password: login.password,
|
||||
hostname: login.hostname,
|
||||
|
||||
// Fields that may be missing
|
||||
title: login['title'] || '',
|
||||
url: login['url'] || '',
|
||||
pinLocked: login['pinLocked'] || false,
|
||||
supplementalInformation: login['supplementalInformation'] || {}
|
||||
};
|
||||
}
|
||||
return {
|
||||
// Save a new login object to localStorage
|
||||
add: function(newLogin) {
|
||||
var loginObj = formatStoredLogin(newLogin);
|
||||
// Filter for logins with the same username and hostname.
|
||||
var existingLoginsForHost = _logins[newLogin.hostname] || [];
|
||||
_logins[newLogin.hostname] =
|
||||
existingLoginsForHost.filter(function(_login) {
|
||||
return _login.username != loginObj.username;
|
||||
});
|
||||
_logins[newLogin.hostname].push(loginObj);
|
||||
saveToLocalStorage();
|
||||
},
|
||||
// Takes a hostname and a callback, and passes it a list of login
|
||||
// objects the user has saved for that domain.
|
||||
getForHostname: function(hostname/*,callback*/) {
|
||||
return (_logins[hostname] || []);
|
||||
},
|
||||
|
||||
// Mainly for debugging purposes.
|
||||
deleteForHostname: function(hostname) {
|
||||
delete _logins[hostname];
|
||||
saveToLocalStorage();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var neverAskObj = (function() {
|
||||
return {
|
||||
// Add a hostname to the list of sites for which we never
|
||||
// prompt to save passwords
|
||||
add: function(siteHostname) {
|
||||
if (!(siteHostname in _neverAsk)) {
|
||||
_neverAsk[siteHostname] = 'all';
|
||||
saveToLocalStorage();
|
||||
}
|
||||
},
|
||||
// Takes a callback, and passes it a list of domains the user
|
||||
// has elected to never save logins on.
|
||||
get: function(/*callback*/) {
|
||||
return _.keys(userData.neverAsk);
|
||||
},
|
||||
// Takes a hostname and a callback. Passes callback a boolean,
|
||||
// indicating if the user *should* be asked about logins
|
||||
// on that domain.
|
||||
checkForHostname: function(hostname/*,callback*/) {
|
||||
return !(hostname in this.get());
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var firstRunObj = (function(){
|
||||
return {
|
||||
wasCompleted: function() {
|
||||
// Takes a callback, and passes it a boolean indicating whether
|
||||
// or not the user has completed the first run flow.
|
||||
// Note that this will return true if the user started the first
|
||||
// run flow but did not complete it.
|
||||
return _didFirstRun;
|
||||
},
|
||||
setIfCompleted: function(firstRunFinished) {
|
||||
//setIfDidFirstRun
|
||||
// Set (or unset) the flag indicating the user has finished the first
|
||||
// run flow.
|
||||
_didFirstRun = firstRunFinished;
|
||||
saveToLocalStorage();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var pinObj = (function() {
|
||||
return {
|
||||
validate: function(testPin) {
|
||||
return testPin == _pin;
|
||||
},
|
||||
get: function() {
|
||||
return _pin;
|
||||
},
|
||||
set: function(newPIN) {
|
||||
_pin = newPIN || null;
|
||||
saveToLocalStorage();
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
Logins: loginsObj,
|
||||
neverAsk: neverAskObj,
|
||||
firstRun: firstRunObj,
|
||||
PIN: pinObj
|
||||
};
|
||||
})(LLStorage);
|
||||
|
||||
// // Takes a callback, and passes it a list of domains the user
|
||||
// // has elected to never save logins on.
|
||||
// function getNeverSaveOnSites(callback) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// callback(_.keys(userData.neverAsk));
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Takes a hostname and a callback, and passes it a list of login
|
||||
// // objects the user has saved for that domain.
|
||||
// function getLoginsForSite(hostname,callback) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// callback(userData.logins[hostname] || []);
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Mainly for debugging purposes.
|
||||
// function deleteLoginsForSite(hostname) {
|
||||
// fetchStoredData(function(userData) {
|
||||
// delete userData[hostname];
|
||||
// updateStoredData(userData);
|
||||
// });
|
||||
// }
|
||||
|
||||
// Returns a string of a comma separated value file containing the hostname, username,
|
||||
// and password for each login the user has saved.
|
||||
function getLoginsCSV(callback) {
|
||||
// Add header
|
||||
var retVal = "hostname,username,password\n";
|
||||
fetchStoredData(function(userData) {
|
||||
for (var item in _.keys(userData.logins)) {
|
||||
for (var login in userData.logins[item]) {
|
||||
retVal += login.hostname + ',' + login.username
|
||||
+ ',' + login.password + '\n';
|
||||
}
|
||||
}
|
||||
callback(retVal);
|
||||
});
|
||||
}
|
||||
|
||||
// Dump localStorage to CSV file, for debugging purposes.
|
||||
function downloadExportDataFile() {
|
||||
// Get entire content of localStorage
|
||||
// NB: This contains all of the user's passwords in plaintext, as well as
|
||||
// their PIN and not-so-useful flags like did_first_run.
|
||||
getLoginsCSV(function(loginsCSV) {
|
||||
// Turn storageObj into a blob
|
||||
var blob = new window.Blob([loginsCSV], {type: 'text/csv'});
|
||||
|
||||
// Creates a link that opens the blob on the background page,
|
||||
// and then clicks it. Cribbed from:
|
||||
// http://stackoverflow.com/questions/4845215/making-a-chrome-extension-download-a-file
|
||||
var a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = 'passwords_dump.csv';
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
delete a;// we don't need this anymore
|
||||
});
|
||||
}
|
|
@ -27,19 +27,20 @@ var User = function(Backbone, _, LinkedSiteCollection) {
|
|||
var User = Backbone.Model.extend({
|
||||
defaults: {
|
||||
version: USER_DATA_VERSIONS[USER_DATA_VERSIONS.length-1],
|
||||
pin: null
|
||||
pin: null,
|
||||
linkedSites: new LinkedSiteCollection()
|
||||
},
|
||||
|
||||
initialize: function(args) {
|
||||
var logins = args.logins || [];
|
||||
delete args.logins;
|
||||
this.set(args);
|
||||
this.linkedSites = new LinkedSiteCollection();
|
||||
this.linkedSites.initalizeFromLoginMap(logins);
|
||||
parse: function(resp) {
|
||||
resp.linkedSites = new LinkedSiteCollection(resp.logins, { parse: true });
|
||||
delete resp.logins;
|
||||
return resp;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
|
||||
toJSON: function(options) {
|
||||
var result = Backbone.Model.prototype.toJSON.apply(this, arguments);
|
||||
delete result.linkedSites;
|
||||
return _.extend(result, { logins: this.get("linkedSites").toJSON(options) });
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
var UserCollection = function(Backbone, _, User, LocalStorage) {
|
||||
|
||||
var UserCollection = Backbone.Collection.extend({
|
||||
model: User,
|
||||
localStorage: LocalStorage
|
||||
});
|
||||
|
||||
return UserCollection;
|
||||
};
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* Backbone localStorage Adapter
|
||||
*
|
||||
* https://github.com/jeromegn/Backbone.localStorage
|
||||
*/
|
||||
var Storage = function(Backbone, _, Storage) {
|
||||
return (function (root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore","backbone"], function(_, Backbone) {
|
||||
// Use global variables if the locals is undefined.
|
||||
return factory(_ || root._, Backbone || root.Backbone);
|
||||
});
|
||||
} else {
|
||||
// RequireJS isn't being used. Assume underscore and backbone is loaded in <script> tags
|
||||
return factory(_, Backbone);
|
||||
}
|
||||
}(this, function(_, Backbone) {
|
||||
// A simple module to replace `Backbone.sync` with *localStorage*-based
|
||||
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
|
||||
// as that.
|
||||
|
||||
// Hold reference to Underscore.js and Backbone.js in the closure in order
|
||||
// to make things work even if they are removed from the global namespace
|
||||
|
||||
// Generate four random hex digits.
|
||||
function S4() {
|
||||
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
||||
};
|
||||
|
||||
// Generate a pseudo-GUID by concatenating random hexadecimal.
|
||||
function guid() {
|
||||
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
|
||||
};
|
||||
|
||||
// Our Store is represented by a single JS object in *localStorage*. Create it
|
||||
// with a meaningful name, like the name you'd give a table.
|
||||
// window.Store is deprectated, use Backbone.LocalStorage instead
|
||||
Backbone.LocalStorage = window.Store = function(name, callback) {
|
||||
this.name = name;
|
||||
var cb = function(store) {
|
||||
this.records = (store && store.split(",")) || [];
|
||||
if (callback) callback();
|
||||
}
|
||||
this.localStorage().getItem(this.name, cb.bind(this));
|
||||
};
|
||||
|
||||
_.extend(Backbone.LocalStorage.prototype, {
|
||||
|
||||
// Save the current state of the **Store** to *localStorage*.
|
||||
save: function(callback) {
|
||||
this.localStorage().setItem(this.name, this.records.join(","), callback);
|
||||
},
|
||||
|
||||
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
|
||||
// have an id of it's own.
|
||||
create: function(model, callback) {
|
||||
if (!model.id) {
|
||||
model.id = guid();
|
||||
model.set(model.idAttribute, model.id);
|
||||
}
|
||||
var cb = _.after(2, function() {
|
||||
callback(model.toJSON());
|
||||
});
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model), cb);
|
||||
this.records.push(model.id.toString());
|
||||
this.save(cb);
|
||||
return;
|
||||
},
|
||||
|
||||
// Update a model by replacing its copy in `this.data`.
|
||||
update: function(model, callback) {
|
||||
var cb = _.after(2, function() {
|
||||
callback(model.toJSON());
|
||||
});
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model), cb);
|
||||
if (!_.include(this.records, model.id.toString())) this.records.push(model.id.toString()); this.save(cb);
|
||||
return;
|
||||
},
|
||||
|
||||
// Retrieve a model from `this.data` by id.
|
||||
find: function(model, callback) {
|
||||
this.localStorage().getItem(this.name+"-"+model.id, function(resp) { callback(this.jsonData(resp)); });
|
||||
return;
|
||||
},
|
||||
|
||||
// Return the array of all models currently in storage.
|
||||
findAll: function(callback) {
|
||||
var result = [];
|
||||
var allFetched = _.after(this.records.length, function() { console.log(result); callback(_(result).chain().compact().value()); });
|
||||
var itemFetched = function(resp) {
|
||||
console.log("READ", resp);
|
||||
result.push(this.jsonData(resp));
|
||||
allFetched();
|
||||
}
|
||||
_(this.records)
|
||||
.map(function(id){return this.localStorage().getItem(this.name+"-"+id, itemFetched.bind(this));}, this);
|
||||
},
|
||||
|
||||
// Delete a model from `this.data`, returning it.
|
||||
destroy: function(model, callback) {
|
||||
var cb = _.after(2, function() {
|
||||
callback(model);
|
||||
});
|
||||
this.localStorage().removeItem(this.name+"-"+model.id, cb);
|
||||
this.records = _.reject(this.records, function(record_id){return record_id == model.id.toString();});
|
||||
this.save(cb);
|
||||
return;
|
||||
},
|
||||
|
||||
localStorage: function() {
|
||||
return Storage;
|
||||
},
|
||||
|
||||
// fix for "illegal access" error on Android when JSON.parse is passed null
|
||||
jsonData: function (data) {
|
||||
return data && JSON.parse(data);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// localSync delegate to the model or collection's
|
||||
// *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 = window.Store.sync = Backbone.localSync = function(method, model, options) {
|
||||
var store = model.localStorage || model.collection.localStorage;
|
||||
|
||||
var resp, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
|
||||
|
||||
var callback = function(resp) {
|
||||
if (resp) {
|
||||
if (options && options.success) options.success(resp);
|
||||
if (syncDfd) syncDfd.resolve();
|
||||
} else {
|
||||
if (options && options.error) options.error("Record not found");
|
||||
if (syncDfd) syncDfd.reject();
|
||||
}
|
||||
|
||||
// add compatibility with $.ajax
|
||||
// always execute callback for success and error
|
||||
if (options && options.complete) options.complete(resp);
|
||||
};
|
||||
|
||||
switch (method) {
|
||||
case "read": model.id != undefined ? store.find(model, callback) : store.findAll(callback); break;
|
||||
case "create": store.create(model, callback); break;
|
||||
case "update": store.update(model, callback); break;
|
||||
case "delete": store.destroy(model, callback); break;
|
||||
}
|
||||
|
||||
return syncDfd && syncDfd.promise();
|
||||
};
|
||||
|
||||
Backbone.ajaxSync = Backbone.sync;
|
||||
|
||||
Backbone.getSyncMethod = function(model) {
|
||||
if(model.localStorage || (model.collection && model.collection.localStorage))
|
||||
{
|
||||
return Backbone.localSync;
|
||||
}
|
||||
|
||||
return Backbone.ajaxSync;
|
||||
};
|
||||
|
||||
// Override 'Backbone.sync' to default to localSync,
|
||||
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
|
||||
Backbone.sync = function(method, model, options) {
|
||||
return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
|
||||
};
|
||||
|
||||
return Backbone.LocalStorage;
|
||||
}));
|
||||
};
|
2
server
2
server
|
@ -1 +1 @@
|
|||
Subproject commit 1b7aa4ce43356f268a5866ed9adecbcfcdfaab4d
|
||||
Subproject commit cee80f7e61d6caf31373c495377df78a3508372c
|
Загрузка…
Ссылка в новой задаче