Update appcache version.
[skeletonkey.git] / core.js
diff --git a/core.js b/core.js
index b1c96f90d8481a9315fc68c82c498086025c8658..3986dae01f959e41201b52d72c39b5e007b85926 100644 (file)
--- a/core.js
+++ b/core.js
  */
 
 (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);
   });
@@ -42,6 +36,14 @@ var SkeletonKey = SkeletonKey || function(doc) {
   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();
 };
 
@@ -56,12 +58,6 @@ SkeletonKey.prototype.ITERATIONS = 1000;
  */
 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
@@ -69,9 +65,25 @@ SkeletonKey.prototype.MIN_LENGTH = 6;
 SkeletonKey.prototype._init = function() {
   this._generateButton.onclick = this._onGenerate.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();
+  }
 };
 
 /**
@@ -85,9 +97,15 @@ SkeletonKey.prototype._onGenerate = function(e) {
   // |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();
 };
 
@@ -102,8 +120,8 @@ SkeletonKey.prototype._capitalizeKey = function(key) {
   // 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);
   }
@@ -135,13 +153,45 @@ SkeletonKey.prototype._capitalizeKey = function(key) {
   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);
 };
 
 /**
@@ -149,20 +199,58 @@ SkeletonKey.prototype._selectPassword = function() {
  * @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';
+}