*/
(function main() {
- if (typeof chrome !== 'undefined') {
- // TODO: load the extension JS
- } else {
- // TODO: load the hosted JS
- }
-
document.addEventListener('DOMContentLoaded', function() {
var controller = new SkeletonKey(document);
});
this._username = doc.getElementById('username');
this._password = doc.getElementById('password');
this._generateButton = doc.getElementById('generate');
+
+ // If this is an extension, use defaults until the Chrome settings are loaded.
+ var win = null;
+ if (!this._isChromeExtension())
+ win = window;
+ this._options = new SkeletonKeyOptions(null, win);
+
+
this._init();
};
*/
SkeletonKey.prototype.KEYSIZE = 256/32;
-/**
- * The minimum length of a password.
- * @const {int}
- */
-SkeletonKey.prototype.MIN_LENGTH = 6;
-
/**
* Initializes event handlers for the page.
* @private
SkeletonKey.prototype._init = function() {
this._generateButton.onclick = this._onGenerate.bind(this);
- this._password.onclick = this._selectPassword.bind(this);
- this._password.labels[0].onclick = this._selectPassword.bind(this);
+ this._master.onkeyup = this._nextFieldInterceptor.bind(this);
+ this._sitekey.onkeyup = this._nextFieldInterceptor.bind(this);
+ this._username.onkeyup = this._nextFieldInterceptor.bind(this);
+
+ if (!this._isTouchDevice()) {
+ this._password.onmousedown = this._selectPassword.bind(this);
+ this._password.onmouseup = function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ }
- this._initChromeExtension();
+ if (this._isChromeExtension()) {
+ this._initChromeExtension();
+ } else {
+ // Chrome extensions will get the first field focused automatically, so only
+ // do it explicitly for hosted pages.
+ this._master.focus();
+ }
};
/**
// |key| is a WordArray of 32-bit words.
var key = CryptoJS.PBKDF2(this._master.value, salt,
{keySize: this.KEYSIZE, iterations: this.ITERATIONS});
+
var hexString = key.toString();
hexString = this._capitalizeKey(hexString);
- this._password.value = hexString;
+
+ var maxLength = this._options.getMaximumPasswordLength();
+ if (hexString.length > maxLength)
+ hexString = hexString.substr(0, maxLength);
+
+ this._password.innerText = hexString;
this._selectPassword();
};
// it as the basis for capitalizing the key.
var capsSource = null;
var keyLength = key.length;
- if (keyLength / 2 <= this.MIN_LENGTH) {
- capsSouce = key.substr(0, keyLength - this.MIN_LENGTH);
+ if (keyLength / 2 <= this._options.getMinimumPasswordLength()) {
+ capsSouce = key.substr(0, keyLength - this._options.getMinimumPasswordLength());
} else {
capsSource = key.substr(keyLength / 2);
}
return newKey;
};
+/**
+ * Checks if the given key event is from the enter key and moves onto the next
+ * field or generates the password.
+ * @param {Event} e
+ * @private
+ */
+SkeletonKey.prototype._nextFieldInterceptor = function(e) {
+ if (e.keyCode != 0xD)
+ return;
+
+ if (this._master.value == "") {
+ this._master.focus();
+ } else if (this._sitekey.value == "") {
+ this._sitekey.focus();
+ } else if (this._username.value == "") {
+ this._username.focus();
+ } else {
+ this._generateButton.click();
+ }
+};
+
/**
* Selects the contents of the generated password.
* @private
*/
SkeletonKey.prototype._selectPassword = function() {
- this._password.focus();
- this._password.select();
+ this._generateButton.blur();
+
+ // Touch devices do not bring up the edit controls (for copy) for
+ // pre-selected text.
+ if (this._isTouchDevice())
+ return;
+
+ var range = document.createRange();
+ range.selectNode(this._password.firstChild); // Select #text node.
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
};
/**
* @private
*/
SkeletonKey.prototype._initChromeExtension = function() {
- return;
- if (typeof chrome == 'undefined' || typeof chrome.extension == 'undefined')
- return;
-
- // getCurrent is undefined for backround pages. Need content script.
- chrome.tabs.getCurrent(function (tab) {
- if (tab == null)
+ var query = {
+ "active": true,
+ "currentWindow": true
+ };
+ chrome.tabs.query(query, function (tabs) {
+ console.log(tabs);
+ if (tabs == null || tabs.length != 1)
return;
- var url = tab.url;
+ var url = tabs[0].url;
if (url == null || url == "")
return;
- var siteKey = url.search(/https?:\/\/(www.?|login|accounts?)\.(.*)\.(com?|net|org|edu|biz|info)?.*/);
- console.log(siteKey);
- });
+ // Use a link to clevely parse the URL into the hostname.
+ var parser = document.createElement("a");
+ parser.href = url;
+ var hostname = parser.hostname.split(".");
+
+ // Filter out common subdomains and TLDs to keep the siteky short and
+ // memorable.
+ ["www", "login", "account", "accounts"].forEach(function(subdomain) {
+ if (hostname[0] == subdomain) {
+ hostname.shift();
+ return;
+ }
+ });
+
+ ["com", "net", "org", "edu", "info"].forEach(function(tld) {
+ if (hostname[hostname.length - 1] == tld) {
+ hostname.pop();
+ return;
+ }
+ });
+
+ this._sitekey.value = hostname.join(".");
+ }.bind(this));
};
+
+/**
+ * Checks if SkeletonKey is running as a Chrome extension.
+ * @returns {bool}
+ * @private
+ */
+SkeletonKey.prototype._isChromeExtension = function() {
+ return typeof chrome !== 'undefined' && typeof chrome.tabs !== 'undefined';
+};
+
+/**
+ * Checks if SkeletonKey is running on a touch device.
+ * @returns {bool}
+ * @private
+ */
+SkeletonKey.prototype._isTouchDevice = function() {
+ return typeof document.createTouch === 'function';
+}