totally broken refactor
This commit is contained in:
Коммит
d5993a17ca
|
@ -2,6 +2,7 @@
|
|||
<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="../infobar/manager.js"></script>
|
||||
<script src="util.js"></script>
|
||||
|
@ -16,6 +17,8 @@
|
|||
<script src="storage.js"></script>
|
||||
<script src="captured_credential_storage.js"></script>
|
||||
<script src="site_configs.js"></script>
|
||||
<script src="linked_site.js"></script>
|
||||
<script src="user.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<body>
|
||||
<!-- Invisible <textarea> used to copy data onto the clipboard -->
|
||||
|
|
|
@ -37,18 +37,14 @@ function formFillCurrentTab() {
|
|||
function getPageDataForPopup(callback) {
|
||||
getActiveTab(function(tab) {
|
||||
var newURL = new Uri(tab.url);
|
||||
getLoginsForSite(newURL.host(),function(logins) {
|
||||
callback(logins);
|
||||
});
|
||||
callback(User.Logins.getForHostname(newURL.host()));
|
||||
});
|
||||
}
|
||||
|
||||
function formFillTab(tab) {
|
||||
var newURL = new Uri(tab.url);
|
||||
getLoginsForSite(newURL.host(),function(logins) {
|
||||
chrome.tabs.sendMessage(tab.id,{
|
||||
type: 'fill_form',
|
||||
login: logins[0]
|
||||
});
|
||||
chrome.tabs.sendMessage(tab.id,{
|
||||
type: 'fill_form',
|
||||
login: User.Logins.getForHostname(newURL.host())[0]
|
||||
});
|
||||
}
|
|
@ -18,17 +18,10 @@ function startFirstRunFlow() {
|
|||
});
|
||||
// Updates the browserAction popup, stores that we haven't
|
||||
// yet completed first run in localStorage.
|
||||
setIfDidFirstRun(false);
|
||||
User.firstRun.setIfCompleted(false);
|
||||
}
|
||||
|
||||
function closeFirstRunTab() {
|
||||
chrome.tabs.remove(firstRunTab);
|
||||
firstRunTab = -1;
|
||||
}
|
||||
|
||||
function firstRunFinished() {
|
||||
// Save the fact that the first run flow has been completed,
|
||||
// so that the splash screen doesn't reopen the next time the
|
||||
// add-on starts, and the regular interface appears in the browserAction.
|
||||
setIfDidFirstRun(true);
|
||||
}
|
||||
}
|
|
@ -10,16 +10,16 @@ var infobarHooks = {
|
|||
'password_observed': function (notificationObj,infobarResponse) {
|
||||
switch (infobarResponse.user_action) {
|
||||
case 'save':
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
break;
|
||||
|
||||
case 'pin_lock':
|
||||
notificationObj.notification.pinLocked = true;
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
break;
|
||||
|
||||
case 'never_for_this_site':
|
||||
neverSaveOnSite(notificationObj.notification.hostname);
|
||||
User.neverAsk.add(notificationObj.notification.hostname);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -46,6 +46,6 @@ var infobarHooks = {
|
|||
}
|
||||
},
|
||||
'update_password': function(notificationObj,infobarResponse) {
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
var LinkedSite = function(Backbone) {
|
||||
|
||||
// LinkedSite constructor
|
||||
// data is:
|
||||
// {
|
||||
// "hostname": "www.mozilla.com",
|
||||
// "realm": "mozilla.com",
|
||||
// "title": Mozilla,
|
||||
// "url": <full url to login page>,
|
||||
// "password": "grëën",
|
||||
// "pinLocked": false,
|
||||
// "username": "gömbottest",
|
||||
// "supplementalInformation": {
|
||||
// "ffNumber": "234324"
|
||||
// }
|
||||
var LinkedSite = Backbone.Model.extend({
|
||||
defaults: {
|
||||
hostname: "",
|
||||
realm: "",
|
||||
title: "",
|
||||
url: "",
|
||||
password: "",
|
||||
pinLocked: false,
|
||||
username: "",
|
||||
supplementalInformation: {}
|
||||
}
|
||||
});
|
||||
|
||||
return LinkedSite;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
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));
|
||||
});
|
||||
});
|
||||
|
||||
return LinkedSiteCollection;
|
||||
};
|
|
@ -16,18 +16,18 @@ 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);
|
||||
|
||||
|
||||
function initGombot() {
|
||||
// Load blacklisted sites from localStorage
|
||||
loadNeverSaveOnSites();
|
||||
// loadNeverSaveOnSites();
|
||||
// Load PIN lock state from localStorage
|
||||
loadLoginsLock();
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
console.log('didFirstRun: ', didFirstRun);
|
||||
if (!didFirstRun) {
|
||||
startFirstRunFlow();
|
||||
}
|
||||
});
|
||||
// loadLoginsLock();
|
||||
if (!User.firstRun.wasCompleted()) {
|
||||
startFirstRunFlow();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -37,178 +37,146 @@ function initGombot() {
|
|||
|
||||
// TODO: merge into command handlers
|
||||
var messageHandlers = {
|
||||
'add_login': function(message,tabID) {
|
||||
var notificationObj = message;
|
||||
// Check to see if the user disabled password saving on this site
|
||||
if (neverSaveOnSites.indexOf(notificationObj.hostname) != -1) return;
|
||||
notificationObj.type = 'password_observed';
|
||||
notificationObj.hash = SHA1(notificationObj.password);
|
||||
// Look for passwords in use on the current site
|
||||
getLoginsForSite(notificationObj.hostname,function(logins) {
|
||||
if (logins === undefined) logins = [];
|
||||
var loginsForSameUsername = logins.filter(
|
||||
function(l) { return l.username == notificationObj.username; }
|
||||
);
|
||||
if (loginsForSameUsername.length == 1) {
|
||||
// User already has a login saved for this site.
|
||||
if (loginsForSameUsername[0].password == notificationObj.password) {
|
||||
// We're just logging into a site with an existing login. Bail.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Prompt user to update password
|
||||
notificationObj.type = 'update_password';
|
||||
// If the existing login stored for this site was PIN locked,
|
||||
// make sure this new one will be also.
|
||||
notificationObj.pinLocked = logins[0].pinLocked;
|
||||
}
|
||||
}
|
||||
// Has the user signed up for a Gombot account?
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
if (didFirstRun) {
|
||||
// Prompt the user to save the login
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: notificationObj
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Browser not associated with Gombot account, offer
|
||||
// to create one/log in.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'signup_nag'
|
||||
}
|
||||
});
|
||||
}
|
||||
'add_login': function(message,tabID) {
|
||||
var notificationObj = message;
|
||||
// Check to see if the user disabled password saving on this site
|
||||
User.NeverAsk.checkForHostname(notificationObj.hostname, function(shouldAsk) {
|
||||
if (!shouldAsk) return;
|
||||
notificationObj.type = 'password_observed';
|
||||
notificationObj.hash = SHA1(notificationObj.password);
|
||||
// Look for passwords in use on the current site
|
||||
getLoginsForSite(notificationObj.hostname,function(logins) {
|
||||
if (logins === undefined) logins = [];
|
||||
var loginsForSameUsername = logins.filter(
|
||||
function(l) { return l.username == notificationObj.username; }
|
||||
);
|
||||
if (loginsForSameUsername.length == 1) {
|
||||
// User already has a login saved for this site.
|
||||
if (loginsForSameUsername[0].password == notificationObj.password) {
|
||||
// We're just logging into a site with an existing login. Bail.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Prompt user to update password
|
||||
notificationObj.type = 'update_password';
|
||||
// If the existing login stored for this site was PIN locked,
|
||||
// make sure this new one will be also.
|
||||
notificationObj.pinLocked = logins[0].pinLocked;
|
||||
}
|
||||
}
|
||||
// Has the user signed up for a Gombot account?
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
if (didFirstRun) {
|
||||
// Prompt the user to save the login
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: notificationObj
|
||||
});
|
||||
// TODO: Send new login to server
|
||||
}
|
||||
else {
|
||||
// Browser not associated with Gombot account, offer
|
||||
// to create one/log in.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'signup_nag'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
'observing_page': function(message,tabID) {
|
||||
// See if there are login forms on this page. If not, there's nothing to do
|
||||
// on the observing_page notification, so bail.
|
||||
if (message.num_login_forms == 0) return;
|
||||
|
||||
// Search for logins for this particular site
|
||||
getLoginsForSite(message.hostname, function(logins) {
|
||||
if (logins.length == 0) return;
|
||||
|
||||
if (logins.length == 1) {
|
||||
// Is the login for this site PIN locked?
|
||||
if (logins[0].pinLocked) {
|
||||
// If it is, show the PIN entry infobar.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
// Include the tabID in the notification so the infobar handler
|
||||
// can trigger autofill in the correct tab.
|
||||
tabID: tabID
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// If it's not, form fill the page now.
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'fill_form',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
|
||||
// If there's only a single login form on the page, we're fine. Otherwise,
|
||||
// see if we were able to record an id or a name for the form when we observed
|
||||
// the login.
|
||||
// TODO: Check to see if the id/name is still valid
|
||||
|
||||
// If we remember specifics about a login form, check to see if it's there.
|
||||
// If it is, offer to autologin.
|
||||
if (logins[0].formEl && (logins[0].formEl.name || logins[0].formEl.id)) {
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'confirm_form_exists',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Prompt user for choice of logins
|
||||
}
|
||||
});
|
||||
},
|
||||
'validate_pin': function(message,tabID,sendResponse) {
|
||||
sendResponse({
|
||||
'is_valid': validatePIN(message.pin)
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
'validate_pin': function(message,tabID,sendResponse) {
|
||||
sendResponse({
|
||||
'is_valid': validatePIN(message.pin)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
// if (request.type && messageHandlers[request.type]) {
|
||||
// messageHandlers[request.type].call(messageHandlers,request.message,sender.tab.id,sendResponse);
|
||||
// }
|
||||
// });
|
||||
|
||||
//
|
||||
// Infobar notifications
|
||||
//
|
||||
|
||||
function displayInfobar(notificationObj) {
|
||||
var infobarPaths = {
|
||||
password_observed: "/infobars/remember_password_infobar.html",
|
||||
update_password: "/infobars/update_password_infobar.html",
|
||||
signup_nag: "/infobars/signup_nag_infobar.html",
|
||||
pin_entry: "/infobars/pin_entry_infobar.html"
|
||||
};
|
||||
// Make sure we have a HTML infobar for this type of notification
|
||||
if (!infobarPaths[notificationObj.notification.type]) return;
|
||||
InfobarManager.run({
|
||||
path: infobarPaths[notificationObj.notification.type],
|
||||
tabId: notificationObj.tabID,
|
||||
height: '32px'
|
||||
}, genHandlerForNotification(notificationObj));
|
||||
var infobarPaths = {
|
||||
password_observed: "/infobars/remember_password_infobar.html",
|
||||
update_password: "/infobars/update_password_infobar.html",
|
||||
signup_nag: "/infobars/signup_nag_infobar.html",
|
||||
pin_entry: "/infobars/pin_entry_infobar.html"
|
||||
};
|
||||
// Make sure we have a HTML infobar for this type of notification
|
||||
if (!infobarPaths[notificationObj.notification.type]) return;
|
||||
InfobarManager.run({
|
||||
path: infobarPaths[notificationObj.notification.type],
|
||||
tabId: notificationObj.tabID,
|
||||
height: '32px'
|
||||
}, genHandlerForNotification(notificationObj));
|
||||
|
||||
function genHandlerForNotification(notificationObj) {
|
||||
return function(err,response) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (!response.type) return;
|
||||
if (!infobarHooks[response.type]) {
|
||||
console.log('Infobar returned unknown response type!');
|
||||
return;
|
||||
}
|
||||
infobarHooks[response.type].call(infobarHooks,notificationObj,response);
|
||||
};
|
||||
}
|
||||
function genHandlerForNotification(notificationObj) {
|
||||
return function(err,response) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (!response.type) return;
|
||||
if (!infobarHooks[response.type]) {
|
||||
console.log('Infobar returned unknown response type!');
|
||||
return;
|
||||
}
|
||||
infobarHooks[response.type].call(infobarHooks,notificationObj,response);
|
||||
};
|
||||
// <<<<<<< HEAD
|
||||
// // Make sure we have a HTML infobar for this type of notification
|
||||
// if (!infobarPaths[notificationObj.notification.type]) return;
|
||||
// InfobarManager.run({
|
||||
// path: infobarPaths[notificationObj.notification.type],
|
||||
// tabId: notificationObj.tabID,
|
||||
// height: '32px'
|
||||
// }, genHandlerForNotification(notificationObj));
|
||||
|
||||
// function genHandlerForNotification(notificationObj) {
|
||||
// return function(err,response) {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// return;
|
||||
// }
|
||||
// if (!response.type) return;
|
||||
// if (!infobarHooks[response.type]) {
|
||||
// console.log('Infobar returned unknown response type!');
|
||||
// return;
|
||||
// }
|
||||
// infobarHooks[response.type].call(infobarHooks,notificationObj,response);
|
||||
// };
|
||||
// }
|
||||
// =======
|
||||
}
|
||||
}
|
||||
|
||||
// Test function that spawns an example infobar on the current active tab.
|
||||
function testInfobarNotification() {
|
||||
getActiveTab(function(tab) {
|
||||
console.log("tab url: ", tab.url,' ', tab);
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tab.id,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
formEl: {},
|
||||
formSubmitURL: "",
|
||||
hash: "bc74f4f071a5a33f00ab88a6d6385b5e6638b86c",
|
||||
hostname: "t.nm.io",
|
||||
httpRealm: null,
|
||||
password: "green",
|
||||
passwordField: {},
|
||||
type: "password_observed",
|
||||
username: "gombottest",
|
||||
usernameField: {}
|
||||
}
|
||||
});
|
||||
getActiveTab(function(tab) {
|
||||
console.log("tab url: ", tab.url,' ', tab);
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tab.id,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
formEl: {},
|
||||
formSubmitURL: "",
|
||||
hash: "bc74f4f071a5a33f00ab88a6d6385b5e6638b86c",
|
||||
hostname: "t.nm.io",
|
||||
httpRealm: null,
|
||||
password: "green",
|
||||
passwordField: {},
|
||||
type: "password_observed",
|
||||
username: "gombottest",
|
||||
usernameField: {}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -216,7 +184,6 @@ function testInfobarNotification() {
|
|||
//
|
||||
|
||||
function validatePIN(_pin) {
|
||||
// If there's no PIN set, accept.
|
||||
if (!loginsLock || !loginsLock.pin) return true;
|
||||
return _pin == loginsLock.pin;
|
||||
// If there's no PIN set, accept. Otherwise, validate.
|
||||
return (!Boolean(User.PIN.get())) || User.PIN.validate(_pin);
|
||||
}
|
||||
|
|
|
@ -6,78 +6,92 @@
|
|||
*
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Getting and saving login data to/from localStorage
|
||||
//
|
||||
|
||||
// List of hostnames the user has asked passwords not to be saved on.
|
||||
// Kept in sync with local storage.
|
||||
var neverSaveOnSites = [];
|
||||
|
||||
// The key for the document in localStorage which holds all of the user
|
||||
// data synced with the server.
|
||||
const LOCAL_STORAGE_KEY = 'gombot_user_data';
|
||||
|
||||
// Other type: 'pin' when PIN locking is enabled.
|
||||
var loginsLock = {
|
||||
type: 'none'
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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.
|
||||
function checkIfDidFirstRun(callback) {
|
||||
chrome.storage.local.get('did_first_run', function(storageObj) {
|
||||
callback(Boolean(storageObj.did_first_run));
|
||||
});
|
||||
}
|
||||
var User = (function(Storage){
|
||||
|
||||
// Set (or unset) the flag indicating the user has finished the first
|
||||
// run flow.
|
||||
function setIfDidFirstRun(firstRunFinished) {
|
||||
chrome.storage.local.set({
|
||||
'did_first_run': Boolean(firstRunFinished)
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the user's PIN in loginsLock and also updates localStorage.
|
||||
function setAndSavePIN(pin) {
|
||||
loginsLock = {
|
||||
'type': 'pin',
|
||||
'pin': pin
|
||||
};
|
||||
saveLoginsLock();
|
||||
}
|
||||
// function updateStoredData(obj) {
|
||||
// var updatedObj = {};
|
||||
// updatedObj[SYNC_DOCUMENT_KEY] = obj;
|
||||
// chrome.storage.local.set(updatedObj);
|
||||
// }
|
||||
|
||||
function loadLoginsLock() {
|
||||
// Load PIN lock state from localStorage
|
||||
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) {
|
||||
if (userData['pin']) {
|
||||
loginsLock = {
|
||||
type: 'pin',
|
||||
pin: userData.pin
|
||||
};
|
||||
}
|
||||
else {
|
||||
loginsLock = {
|
||||
type: 'none'
|
||||
};
|
||||
}
|
||||
_logins = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_pin = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_neverAsk = userData[SYNC_DOCUMENT_KEY].neverAsk;
|
||||
_didFirstRun = userData['did_first_run'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function saveLoginsLock() {
|
||||
// Persist loginsLock to localStorage, to preserve PIN across sessions
|
||||
fetchStoredData(function(userData) {
|
||||
userData['pin'] = loginsLock.pin || null;
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
// Load from localStorage into memory when extension starts.
|
||||
loadFromLocalStorage();
|
||||
|
||||
function formatStoredLogin(login) {
|
||||
return {
|
||||
var userData = {};
|
||||
|
||||
var loginsObj = (function() {
|
||||
function formatStoredLogin(login) {
|
||||
return {
|
||||
username: login.username,
|
||||
password: login.password,
|
||||
hostname: login.hostname,
|
||||
|
@ -87,120 +101,159 @@ function formatStoredLogin(login) {
|
|||
url: login['url'] || '',
|
||||
pinLocked: login['pinLocked'] || false,
|
||||
supplementalInformation: login['supplementalInformation'] || {}
|
||||
};
|
||||
}
|
||||
|
||||
function fetchStoredData(callback) {
|
||||
chrome.storage.local.get(LOCAL_STORAGE_KEY, function(storageObj) {
|
||||
var userData = storageObj[LOCAL_STORAGE_KEY];
|
||||
if (userData === undefined) {
|
||||
userData = {
|
||||
version: "identity.mozilla.com/gombot/v1/userData",
|
||||
logins: {},
|
||||
pin: loginsLock.pin || null,
|
||||
neverAsk: {}
|
||||
};
|
||||
}
|
||||
callback(userData);
|
||||
});
|
||||
}
|
||||
|
||||
function updateStoredData(obj) {
|
||||
var updatedObj = {};
|
||||
updatedObj[LOCAL_STORAGE_KEY] = obj;
|
||||
chrome.storage.local.set(updatedObj);
|
||||
}
|
||||
|
||||
// Save a new login object to localStorage
|
||||
function saveLoginToStorage(newLogin) {
|
||||
var loginObj = formatStoredLogin(newLogin);
|
||||
fetchStoredData(function(userData) {
|
||||
};
|
||||
}
|
||||
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 = userData.logins[newLogin.hostname] || [];
|
||||
userData.logins[newLogin.hostname] =
|
||||
existingLoginsForHost.filter(function(_login) {
|
||||
return _login.username != loginObj.username;
|
||||
});
|
||||
userData.logins[newLogin.hostname].push(loginObj);
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
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] || []);
|
||||
},
|
||||
|
||||
function loadNeverSaveOnSites() {
|
||||
getNeverSaveOnSites(function(siteNames) { neverSaveOnSites = siteNames; });
|
||||
}
|
||||
// Mainly for debugging purposes.
|
||||
deleteForHostname: function(hostname) {
|
||||
delete _logins[hostname];
|
||||
saveToLocalStorage();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Add a hostname to the list of sites for which we never
|
||||
// prompt to save passwords
|
||||
function neverSaveOnSite(siteHostname) {
|
||||
fetchStoredData(function(userData) {
|
||||
if (!(siteHostname in userData.neverAsk)) {
|
||||
userData.neverAsk[siteHostname] = 'all';
|
||||
updateStoredData(userData);
|
||||
loadNeverSaveOnSites();
|
||||
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());
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
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();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// 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] || []);
|
||||
});
|
||||
}
|
||||
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);
|
||||
|
||||
// Mainly for debugging purposes.
|
||||
function deleteLoginsForSite(hostname) {
|
||||
fetchStoredData(function(userData) {
|
||||
delete userData[hostname];
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
// // 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);
|
||||
});
|
||||
// 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'});
|
||||
// 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
|
||||
});
|
||||
// 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
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
var User = function(Backbone, _, LinkedSiteCollection) {
|
||||
|
||||
const USER_DATA_VERSIONS = [
|
||||
"identity.mozilla.com/gombot/v1/userData"
|
||||
];
|
||||
|
||||
|
||||
// attributes should be something like:
|
||||
// {
|
||||
// "version": "identity.mozilla.com/gombot/v1/userData",
|
||||
// "logins": {
|
||||
|
||||
// "mozilla.com":
|
||||
// [{
|
||||
// "hostname": "mozilla.com",
|
||||
// "title": <Site Name>,
|
||||
// "url": <full url to login page>,
|
||||
// "password": "grëën",
|
||||
// "pinLocked": false,
|
||||
// "username": "gömbottest",
|
||||
// "supplementalInformation": {
|
||||
// "ffNumber": "234324"
|
||||
// }}]
|
||||
// },
|
||||
// "pin": "1234"
|
||||
// }
|
||||
var User = Backbone.Model.extend({
|
||||
defaults: {
|
||||
version: USER_DATA_VERSIONS[USER_DATA_VERSIONS.length-1],
|
||||
pin: null
|
||||
},
|
||||
|
||||
initialize: function(args) {
|
||||
var logins = args.logins || [];
|
||||
delete args.logins;
|
||||
this.set(args);
|
||||
this.linkedSites = new LinkedSiteCollection();
|
||||
this.linkedSites.initalizeFromLoginMap(logins);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
|
||||
|
||||
|
||||
});
|
||||
return User;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// var User = function(Storage, LinkedSite) {
|
||||
|
||||
// // mapping of user ids -> User objects
|
||||
// var users = {};
|
||||
|
||||
// const USERS_KEY = "users";
|
||||
|
||||
|
||||
// // User object constructor
|
||||
|
||||
// var User = function(attributes) {
|
||||
// attributes = attributes || {};
|
||||
// attributes.version = attributes.version ||
|
||||
// attributes.logins = attributes.logins || [];
|
||||
// attributes.pin = attributes.pin || "";
|
||||
|
||||
// this.logins = attributes.logins.forEach(function(loginData))
|
||||
// this.attributes = { = data;
|
||||
// }
|
||||
|
||||
// User.prototype.save = function(callback) {
|
||||
// Storage.get(USERS_KEY, function(userDataCollection) {
|
||||
// userDataCollection[this.id] = this.data;
|
||||
// Storage.set(USERS_KEY, userDataCollection, callback);
|
||||
// };
|
||||
// };
|
||||
|
||||
// User.prototype.delete = function(callback) {
|
||||
// Storage.get(USERS_KEY, function(userDataCollection) {
|
||||
// delete userDataCollection[this.id];
|
||||
// Storage.set(USERS_KEY, userDataCollection, callback);
|
||||
// };
|
||||
// };
|
||||
|
||||
// User.init = function(callback) {
|
||||
// Storage.get(USERS_KEY, function(userDataCollection) {
|
||||
// var ids;
|
||||
// if (result) {
|
||||
// ids = Object.getOwnPropertyNames(result);
|
||||
// ids.forEach(function(id) {
|
||||
// users[id] = new User(userDataCollection[id]);
|
||||
// });
|
||||
// }
|
||||
// callback();
|
||||
// }
|
||||
// }
|
||||
|
||||
// User.fetchAll() = function() {
|
||||
// return users;
|
||||
// };
|
||||
|
||||
// User.fetch(id) = function() {
|
||||
// return users[id];
|
||||
// };
|
||||
|
||||
// return User;
|
||||
// }
|
2
infobar
2
infobar
|
@ -1 +1 @@
|
|||
Subproject commit 0e3bb2627c521bf79c69a8f60e4bfa9a8e9bc82e
|
||||
Subproject commit fbe957e8631755100661d321a4eb640d797b6254
|
|
@ -0,0 +1,42 @@
|
|||
// Backbone.js 0.9.9
|
||||
|
||||
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://backbonejs.org
|
||||
(function(){var k=this,y=k.Backbone,h=[],z=h.push,r=h.slice,A=h.splice,g;g="undefined"!==typeof exports?exports:k.Backbone={};g.VERSION="0.9.9";var e=k._;!e&&"undefined"!==typeof require&&(e=require("underscore"));g.$=k.jQuery||k.Zepto||k.ender;g.noConflict=function(){k.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var s=/\s+/,n=function(a,b,c,d){if(!c)return!0;if("object"===typeof c)for(var f in c)a[b].apply(a,[f,c[f]].concat(d));else if(s.test(c)){c=c.split(s);f=0;for(var e=c.length;f<
|
||||
e;f++)a[b].apply(a,[c[f]].concat(d))}else return!0},t=function(a,b,c){var d,a=-1,f=b.length;switch(c.length){case 0:for(;++a<f;)(d=b[a]).callback.call(d.ctx);break;case 1:for(;++a<f;)(d=b[a]).callback.call(d.ctx,c[0]);break;case 2:for(;++a<f;)(d=b[a]).callback.call(d.ctx,c[0],c[1]);break;case 3:for(;++a<f;)(d=b[a]).callback.call(d.ctx,c[0],c[1],c[2]);break;default:for(;++a<f;)(d=b[a]).callback.apply(d.ctx,c)}},h=g.Events={on:function(a,b,c){if(!n(this,"on",a,[b,c])||!b)return this;this._events||(this._events=
|
||||
{});(this._events[a]||(this._events[a]=[])).push({callback:b,context:c,ctx:c||this});return this},once:function(a,b,c){if(!n(this,"once",a,[b,c])||!b)return this;var d=this,f=e.once(function(){d.off(a,f);b.apply(this,arguments)});f._callback=b;this.on(a,f,c);return this},off:function(a,b,c){var d,f,l,g,i,m,h,j;if(!this._events||!n(this,"off",a,[b,c]))return this;if(!a&&!b&&!c)return this._events={},this;g=a?[a]:e.keys(this._events);i=0;for(m=g.length;i<m;i++)if(a=g[i],d=this._events[a]){l=[];if(b||
|
||||
c){h=0;for(j=d.length;h<j;h++)f=d[h],(b&&b!==(f.callback._callback||f.callback)||c&&c!==f.context)&&l.push(f)}this._events[a]=l}return this},trigger:function(a){if(!this._events)return this;var b=r.call(arguments,1);if(!n(this,"trigger",a,b))return this;var c=this._events[a],d=this._events.all;c&&t(this,c,b);d&&t(this,d,arguments);return this},listenTo:function(a,b,c){var d=this._listeners||(this._listeners={}),f=a._listenerId||(a._listenerId=e.uniqueId("l"));d[f]=a;a.on(b,c||this,this);return this},
|
||||
stopListening:function(a,b,c){var d=this._listeners;if(d){if(a)a.off(b,c,this),!b&&!c&&delete d[a._listenerId];else{for(var f in d)d[f].off(null,null,this);this._listeners={}}return this}}};h.bind=h.on;h.unbind=h.off;e.extend(g,h);var o=g.Model=function(a,b){var c,d=a||{};this.cid=e.uniqueId("c");this.changed={};this.attributes={};this._changes=[];b&&b.collection&&(this.collection=b.collection);b&&b.parse&&(d=this.parse(d));(c=e.result(this,"defaults"))&&e.defaults(d,c);this.set(d,{silent:!0});this._currentAttributes=
|
||||
e.clone(this.attributes);this._previousAttributes=e.clone(this.attributes);this.initialize.apply(this,arguments)};e.extend(o.prototype,h,{changed:null,idAttribute:"id",initialize:function(){},toJSON:function(){return e.clone(this.attributes)},sync:function(){return g.sync.apply(this,arguments)},get:function(a){return this.attributes[a]},escape:function(a){return e.escape(this.get(a))},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,f;if(null==a)return this;e.isObject(a)?(f=a,c=
|
||||
b):(f={})[a]=b;var a=c&&c.silent,l=c&&c.unset;if(!this._validate(f,c))return!1;this.idAttribute in f&&(this.id=f[this.idAttribute]);var g=this.attributes;for(d in f)b=f[d],l?delete g[d]:g[d]=b,this._changes.push(d,b);this._hasComputed=!1;a||this.change(c);return this},unset:function(a,b){return this.set(a,void 0,e.extend({},b,{unset:!0}))},clear:function(a){var b={},c;for(c in this.attributes)b[c]=void 0;return this.set(b,e.extend({},a,{unset:!0}))},fetch:function(a){a=a?e.clone(a):{};void 0===a.parse&&
|
||||
(a.parse=!0);var b=this,c=a.success;a.success=function(d){if(!b.set(b.parse(d),a))return false;c&&c(b,d,a)};return this.sync("read",this,a)},save:function(a,b,c){var d,f,g;null==a||e.isObject(a)?(d=a,c=b):null!=a&&((d={})[a]=b);c=c?e.clone(c):{};if(c.wait){if(d&&!this._validate(d,c))return!1;f=e.clone(this.attributes)}a=e.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c)||!d&&!this._validate(null,c))return!1;var q=this,i=c.success;c.success=function(a){g=true;var b=q.parse(a);c.wait&&(b=e.extend(d||
|
||||
{},b));if(!q.set(b,c))return false;i&&i(q,a,c)};b=this.isNew()?"create":c.patch?"patch":"update";"patch"==b&&(c.attrs=d);b=this.sync(b,this,c);!g&&c.wait&&(this.clear(a),this.set(f,a));return b},destroy:function(a){var a=a?e.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};a.success=function(f){(a.wait||b.isNew())&&d();c&&c(b,f,a)};if(this.isNew())return a.success(),!1;var f=this.sync("delete",this,a);a.wait||d();return f},url:function(){var a=e.result(this,"urlRoot")||
|
||||
e.result(this.collection,"url")||u();return this.isNew()?a:a+("/"===a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){var b=this._changing;this._changing=!0;var c=this._computeChanges(!0);this._pending=!!c.length;for(var d=c.length-2;0<=d;d-=2)this.trigger("change:"+c[d],this,c[d+1],a);if(b)return this;for(;this._pending;)this._pending=!1,this.trigger("change",
|
||||
this,a),this._previousAttributes=e.clone(this.attributes);this._changing=!1;return this},hasChanged:function(a){this._hasComputed||this._computeChanges();return null==a?!e.isEmpty(this.changed):e.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?e.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,f;for(f in a)if(!e.isEqual(d[f],b=a[f]))(c||(c={}))[f]=b;return c},_computeChanges:function(a){this.changed={};for(var b={},c=[],d=this._currentAttributes,f=this._changes,
|
||||
e=f.length-2;0<=e;e-=2){var g=f[e],i=f[e+1];b[g]||(b[g]=!0,d[g]!==i&&(this.changed[g]=i,a&&(c.push(g,i),d[g]=i)))}a&&(this._changes=[]);this._hasComputed=!0;return c},previous:function(a){return null==a||!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return e.clone(this._previousAttributes)},_validate:function(a,b){if(!this.validate)return!0;var a=e.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error&&b.error(this,c,b);this.trigger("error",
|
||||
this,c,b);return!1}});var p=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);void 0!==b.comparator&&(this.comparator=b.comparator);this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,e.extend({silent:!0},b))};e.extend(p.prototype,h,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},sync:function(){return g.sync.apply(this,arguments)},add:function(a,b){var c,d,f,g,h=b&&b.at,i=null==(b&&b.sort)?!0:b.sort,a=e.isArray(a)?
|
||||
a.slice():[a];for(c=a.length-1;0<=c;c--)(d=this._prepareModel(a[c],b))?(a[c]=d,(f=null!=d.id&&this._byId[d.id])||this._byCid[d.cid]?(b&&(b.merge&&f)&&(f.set(d.attributes,b),g=i),a.splice(c,1)):(d.on("all",this._onModelEvent,this),this._byCid[d.cid]=d,null!=d.id&&(this._byId[d.id]=d))):(this.trigger("error",this,a[c],b),a.splice(c,1));a.length&&(g=i);this.length+=a.length;c=[null!=h?h:this.models.length,0];z.apply(c,a);A.apply(this.models,c);g&&(this.comparator&&null==h)&&this.sort({silent:!0});if(b&&
|
||||
b.silent)return this;for(;d=a.shift();)d.trigger("add",d,this,b);return this},remove:function(a,b){var c,d,f,g;b||(b={});a=e.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],f=this.indexOf(g),this.models.splice(f,1),this.length--,b.silent||(b.index=f,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,e.extend({at:this.length},b));return a},pop:function(a){var b=
|
||||
this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,e.extend({at:0},b));return a},shift:function(a){var b=this.at(0);this.remove(b,a);return b},slice:function(a,b){return this.models.slice(a,b)},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]||this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return e.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},
|
||||
sort:function(a){if(!this.comparator)throw Error("Cannot sort a set without a comparator");e.isString(this.comparator)||1===this.comparator.length?this.models=this.sortBy(this.comparator,this):this.models.sort(e.bind(this.comparator,this));(!a||!a.silent)&&this.trigger("sort",this,a);return this},pluck:function(a){return e.invoke(this.models,"get",a)},update:function(a,b){var c,d,f,g,h=[],i=[],m={},j=this.model.prototype.idAttribute,b=e.extend({add:!0,merge:!0,remove:!0},b);b.parse&&(a=this.parse(a));
|
||||
e.isArray(a)||(a=a?[a]:[]);if(b.add&&!b.remove)return this.add(a,b);d=0;for(f=a.length;d<f;d++)c=a[d],g=this.get(c.id||c.cid||c[j]),b.remove&&g&&(m[g.cid]=!0),(b.add&&!g||b.merge&&g)&&h.push(c);if(b.remove){d=0;for(f=this.models.length;d<f;d++)c=this.models[d],m[c.cid]||i.push(c)}i.length&&this.remove(i,b);h.length&&this.add(h,b);return this},reset:function(a,b){b||(b={});b.parse&&(a=this.parse(a));for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);b.previousModels=this.models;
|
||||
this._reset();a&&this.add(a,e.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?e.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d){b[a.update?"update":"reset"](d,a);c&&c(b,d,a)};return this.sync("read",this,a)},create:function(a,b){var c=this,b=b?e.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(a,b,e){e.wait&&c.add(a,e);d&&d(a,b,e)};a.save(null,b);return a},
|
||||
parse:function(a){return a},clone:function(){return new this.constructor(this.models)},chain:function(){return e(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId={};this._byCid={}},_prepareModel:function(a,b){if(a instanceof o)return a.collection||(a.collection=this),a;b||(b={});b.collection=this;var c=new this.model(a,b);return!c._validate(a,b)?!1:c},_removeReference:function(a){this===a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,
|
||||
b,c,d){("add"===a||"remove"===a)&&c!==this||("destroy"===a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],null!=b.id&&(this._byId[b.id]=b)),this.trigger.apply(this,arguments))}});e.each("forEach each map collect reduce foldl inject reduceRight foldr find detect filter select reject every all some any include contains invoke max min sortedIndex toArray size first head take initial rest tail last without indexOf shuffle lastIndexOf isEmpty".split(" "),
|
||||
function(a){p.prototype[a]=function(){var b=r.call(arguments);b.unshift(this.models);return e[a].apply(e,b)}});e.each(["groupBy","countBy","sortBy"],function(a){p.prototype[a]=function(b,c){var d=e.isFunction(b)?b:function(a){return a.get(b)};return e[a](this.models,d,c)}});var v=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/\((.*?)\)/g,C=/:\w+/g,D=/\*\w+/g,E=/[\-{}\[\]+?.,\\\^$|#\s]/g;e.extend(v.prototype,h,{initialize:function(){},
|
||||
route:function(a,b,c){e.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,e.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b);return this},_bindRoutes:function(){if(this.routes)for(var a,b=e.keys(this.routes);null!=(a=b.pop());)this.route(a,this.routes[a])},_routeToRegExp:function(a){a=a.replace(E,"\\$&").replace(B,
|
||||
"(?:$1)?").replace(C,"([^/]+)").replace(D,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var j=g.History=function(){this.handlers=[];e.bindAll(this,"checkUrl");"undefined"!==typeof window&&(this.location=window.location,this.history=window.history)},w=/^[#\/]|\s+$/g,F=/^\/+|\/+$/g,G=/msie [\w.]+/,H=/\/$/;j.started=!1;e.extend(j.prototype,h,{interval:50,getHash:function(a){return(a=(a||this).location.href.match(/#(.*)$/))?a[1]:""},getFragment:function(a,
|
||||
b){if(null==a)if(this._hasPushState||!this._wantsHashChange||b){var a=this.location.pathname,c=this.root.replace(H,"");a.indexOf(c)||(a=a.substr(c.length))}else a=this.getHash();return a.replace(w,"")},start:function(a){if(j.started)throw Error("Backbone.history has already been started");j.started=!0;this.options=e.extend({},{root:"/"},this.options,a);this.root=this.options.root;this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!(!this.options.pushState||
|
||||
!this.history||!this.history.pushState);var a=this.getFragment(),b=document.documentMode,b=G.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b);this.root=("/"+this.root+"/").replace(F,"/");b&&this._wantsHashChange&&(this.iframe=g.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a));this._hasPushState?g.$(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?g.$(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&
|
||||
(this._checkUrlInterval=setInterval(this.checkUrl,this.interval));this.fragment=a;a=this.location;b=a.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),this.location.replace(this.root+this.location.search+"#"+this.fragment),!0;this._wantsPushState&&(this._hasPushState&&b&&a.hash)&&(this.fragment=this.getHash().replace(w,""),this.history.replaceState({},document.title,this.root+this.fragment+
|
||||
a.search));if(!this.options.silent)return this.loadUrl()},stop:function(){g.$(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);j.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a===this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a===this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},
|
||||
loadUrl:function(a){var b=this.fragment=this.getFragment(a);return e.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!j.started)return!1;if(!b||!0===b)b={trigger:b};a=this.getFragment(a||"");if(this.fragment!==a){this.fragment=a;var c=this.root+a;if(this._hasPushState)this.history[b.replace?"replaceState":"pushState"]({},document.title,c);else if(this._wantsHashChange)this._updateHash(this.location,a,b.replace),this.iframe&&a!==this.getFragment(this.getHash(this.iframe))&&
|
||||
(b.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,a,b.replace));else return this.location.assign(c);b.trigger&&this.loadUrl(a)}},_updateHash:function(a,b,c){c?(c=a.href.replace(/(javascript:|#).*$/,""),a.replace(c+"#"+b)):a.hash="#"+b}});g.history=new j;var x=g.View=function(a){this.cid=e.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},I=/^(\S+)\s*(.*)$/,J="model collection el id attributes className tagName events".split(" ");
|
||||
e.extend(x.prototype,h,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},make:function(a,b,c){a=document.createElement(a);b&&g.$(a).attr(b);null!=c&&g.$(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof g.$?a:g.$(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=e.result(this,
|
||||
"events"))){this.undelegateEvents();for(var b in a){var c=a[b];e.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(I),f=d[1],d=d[2],c=e.bind(c,this),f=f+(".delegateEvents"+this.cid);""===d?this.$el.bind(f,c):this.$el.delegate(d,f,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=e.extend({},e.result(this,"options"),a));e.extend(this,e.pick(a,J));this.options=a},_ensureElement:function(){if(this.el)this.setElement(e.result(this,
|
||||
"el"),!1);else{var a=e.extend({},e.result(this,"attributes"));this.id&&(a.id=e.result(this,"id"));this.className&&(a["class"]=e.result(this,"className"));this.setElement(this.make(e.result(this,"tagName"),a),!1)}}});var K={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=K[a];e.defaults(c||(c={}),{emulateHTTP:g.emulateHTTP,emulateJSON:g.emulateJSON});var f={type:d,dataType:"json"};c.url||(f.url=e.result(b,"url")||u());if(null==c.data&&b&&("create"===
|
||||
a||"update"===a||"patch"===a))f.contentType="application/json",f.data=JSON.stringify(c.attrs||b.toJSON(c));c.emulateJSON&&(f.contentType="application/x-www-form-urlencoded",f.data=f.data?{model:f.data}:{});if(c.emulateHTTP&&("PUT"===d||"DELETE"===d||"PATCH"===d)){f.type="POST";c.emulateJSON&&(f.data._method=d);var h=c.beforeSend;c.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d);if(h)return h.apply(this,arguments)}}"GET"!==f.type&&!c.emulateJSON&&(f.processData=!1);var j=c.success;
|
||||
c.success=function(a,d,e){j&&j(a,d,e);b.trigger("sync",b,a,c)};var i=c.error;c.error=function(a){i&&i(b,a,c);b.trigger("error",b,a,c)};a=g.ajax(e.extend(f,c));b.trigger("request",b,a,c);return a};g.ajax=function(){return g.$.ajax.apply(g.$,arguments)};o.extend=p.extend=v.extend=x.extend=j.extend=function(a,b){var c=this,d;d=a&&e.has(a,"constructor")?a.constructor:function(){c.apply(this,arguments)};e.extend(d,c,b);var f=function(){this.constructor=d};f.prototype=c.prototype;d.prototype=new f;a&&e.extend(d.prototype,
|
||||
a);d.__super__=c.prototype;return d};var u=function(){throw Error('A "url" property or function must be specified');}}).call(this);
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "Gombot Alpha",
|
||||
"manifest_version": 2,
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.7",
|
||||
"description": "Tired of remembering your usernames and passwords? Let Gombot do the work for you!",
|
||||
"permissions": [
|
||||
"tabs",
|
||||
|
|
|
@ -38,8 +38,8 @@ $(document).ready(function() {
|
|||
|
||||
var backgroundPage = chrome.extension.getBackgroundPage();
|
||||
// Set user PIN
|
||||
backgroundPage.setAndSavePIN($('[name="pin"]').get()[0].value);
|
||||
backgroundPage.firstRunFinished();
|
||||
backgroundPage.User.PIN.set($('[name="pin"]').get()[0].value);
|
||||
backgroundPage.User.firstRun.setIfCompleted(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче