This commit is contained in:
Chris Karlof 2013-01-10 18:18:51 -08:00
Родитель bdc1bc3e00 bfa352d71b
Коммит d5993a17ca
13 изменённых файлов: 565 добавлений и 355 удалений

Просмотреть файл

@ -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);
}
}

30
background/linked_site.js Normal file
Просмотреть файл

@ -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
});
}

112
background/user.js Normal file
Просмотреть файл

@ -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;
// }

@ -1 +1 @@
Subproject commit 0e3bb2627c521bf79c69a8f60e4bfa9a8e9bc82e
Subproject commit fbe957e8631755100661d321a4eb640d797b6254

42
lib/backbone-min.js поставляемый Normal file
Просмотреть файл

@ -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);
}
});
});