Permalink
Browse files

Refinements to indicator. Added indication for esc and return, and a …

…flash when return is hit but no link is active. Indicator turns green when one is.
  • Loading branch information...
1 parent c4c4d03 commit d68b4d5f076a47c90c2bcb0b9296953d290e9212 @dbergey committed Nov 1, 2009
Showing with 191 additions and 125 deletions.
  1. BIN type_to_navigate.crx
  2. +1 −1 type_to_navigate/manifest.json
  3. +189 −123 type_to_navigate/type_to_navigate.user.js
  4. +1 −1 updates.xml
View
Binary file not shown.
@@ -1,6 +1,6 @@
{
"name": "Type-To-Navigate",
- "version": "0.27",
+ "version": "0.28",
"description": "Enables type-to-navigate, where you can select links/anything just by typing, then hit return to follow a link.",
@@ -8,136 +8,202 @@
// ==/UserScript==
(function() {
- var searchString = '';
- var nextSearchString = '';
- var keyupTimeout;
- var indicator;
- var indicatorInner;
- function trim(str) { return str.match(/^\s*(.*?)\s*$/)[1]; };
-
- function focusedElement() {
- var el = document.activeElement;
- var computedStyle = window.getComputedStyle(el);
- return (( el.tagName.match(/INPUT|TEXTAREA/) && (el.getAttribute('type') || '').match(/^|text|search|password$/) ) || el.getAttribute('contenteditable') == 'true' || computedStyle['-webkit-user-modify'] != 'read-only') ? el : false;
- };
-
- function focusSelectedLink(str) {
- var s = window.getSelection();
- // get element
- var el = s.anchorNode || false;
- while ( el && el.tagName != 'A' ) el = el.parentNode;
- if ( el && el.tagName == 'A' ) {
- el.focus();
- } else if ( s.rangeCount ) {
- // get selection
- var range = document.createRange();
- range.setStart(s.anchorNode, s.anchorOffset);
- range.setEnd(s.extentNode, s.extentOffset);
- // defocus (side-effect: deselects)
- document.activeElement.blur();
- // reselect selection
- s.addRange(range);
- } else {
- document.activeElement.blur();
- }
- };
-
- function createIndicator() {
- // create indicator
- var container = document.createElement('div');
- container.innerHTML = '<div id="type_to_select_keys">\
- <style>\
- #type_to_select_keys {\
- position: fixed;\
- left: 0;\
- right: 0;\
- bottom: 10%;\
- text-align: center;\
- opacity: 0;\
- font: 18px arial;\
- -webkit-transition: opacity .25s linear;\
- z-index: 9999999;\
- }\
- #type_to_select_keys_inner {\
- background: rgba(0, 0, 0, 0.75);\
- -webkit-border-radius: 8px;\
- border: 2px solid rgba(255, 255, 255, 0.75);\
- -webkit-box-shadow: 0 3px 25px rgba(0, 0, 0, 0.75);\
- margin: 0 auto;\
- display: inline-block;\
- padding: 8px;\
- color: white;\
- }\
- </style>\
- <div id="type_to_select_keys_inner"></div>\
- </div>';
- document.body.appendChild(indicator = container.childNodes[0]);
- indicatorInner = document.getElementById('type_to_select_keys_inner');
- }
-
- if ( document.readyState == 'complete' ) createIndicator();
- else window.addEventListener('load', function() {
- createIndicator();
- });
+ var ext = {
- // handle command-g & esc
- window.addEventListener('keydown', function(e) {
- e.cmdKey = e.metaKey && !e.ctrlKey;
- e.character = String.fromCharCode(e.keyCode);
+ searchString: '',
+ nextSearchString: '',
+ keyupTimeout: null,
- // handle esc in fields (blur)
- if ( focusedElement() && e.keyCode == 27 ) {
- focusedElement().blur();
- return false;
- }
+ indicator: null,
+ indicatorInner: null,
+ indicatorTimeout: null,
+ indicatorFadeTimeout: null,
+ indicatorFlashTimeout: null,
- // if cmd-g and we have go to next
- var s = window.getSelection();
- if ( e.character == 'G' && e.cmdKey && s.rangeCount && trim(String(s).toLowerCase()) == trim(nextSearchString.toLowerCase()) ) {
- window.find(nextSearchString, false, e.shiftKey, true, false, true, false);
- focusSelectedLink(nextSearchString);
- event.preventDefault();
- event.stopPropagation();
- return false;
- }
- });
+ trim: function(str) { return str.match(/^\s*(.*?)\s*$/)[1]; },
+
+ focusedElement: function() {
+ var el = document.activeElement;
+ var computedStyle = window.getComputedStyle(el);
+ return (( el.tagName.match(/INPUT|TEXTAREA/) && (el.getAttribute('type') || '').match(/^|text|search|password$/) ) || el.getAttribute('contenteditable') == 'true' || computedStyle['-webkit-user-modify'] != 'read-only') ? el : false;
+ },
+ focusSelectedLink: function(str) {
+ var s = window.getSelection();
+ // get element
+ var el = s.anchorNode || false;
+ while ( el && el.tagName != 'A' ) el = el.parentNode;
+ if ( el && el.tagName == 'A' ) {
+ ext.indicatorInner.className = 'green';
+ el.focus();
+ } else if ( s.rangeCount ) {
+ ext.indicatorInner.className = '';
+ // get selection
+ var range = document.createRange();
+ range.setStart(s.anchorNode, s.anchorOffset);
+ range.setEnd(s.extentNode, s.extentOffset);
+ // defocus (side-effect: deselects)
+ document.activeElement.blur();
+ // reselect selection
+ s.addRange(range);
+ } else {
+ ext.indicatorInner.className = '';
+ document.activeElement.blur();
+ }
+ },
+ createIndicator: function() {
+ // create indicator
+ var container = document.createElement('div');
+ container.innerHTML = '<div id="type_to_select_keys">\
+ <style>\
+ #type_to_select_keys {\
+ position: fixed;\
+ left: 0;\
+ right: 0;\
+ bottom: 10%;\
+ text-align: center;\
+ opacity: 0;\
+ font: 18px arial;\
+ -webkit-transition: opacity .25s linear;\
+ z-index: 9999999;\
+ display: none;\
+ }\
+ #type_to_select_keys_inner {\
+ background: rgba(0, 0, 0, 0.75);\
+ -webkit-border-radius: 8px;\
+ border: 2px solid rgba(255, 255, 255, 0.75);\
+ -webkit-box-shadow: 0 3px 25px rgba(0, 0, 0, 0.75);\
+ margin: 0 auto;\
+ display: inline-block;\
+ padding: 8px;\
+ color: white;\
+ }\
+ #type_to_select_keys_inner.red {\
+ background: rgba(255, 0, 0, 0.75);\
+ }\
+ #type_to_select_keys_inner.green {\
+ background: rgba(0, 191, 0, 0.75);\
+ }\
+ </style>\
+ <div id="type_to_select_keys_inner"></div>\
+ </div>';
+ document.body.appendChild(ext.indicator = container.childNodes[0]);
+ ext.indicatorInner = document.getElementById('type_to_select_keys_inner');
+ },
+ displayInIndicator: function(str) {
+ console.log(str);
+ clearTimeout(ext.indicatorTimeout);
+ clearTimeout(ext.indicatorFadeTimeout);
+ if ( ext.indicator ) {
+ ext.indicatorInner.innerHTML = str;
+ ext.indicator.style['-webkit-transition'] = 'none';
+ ext.indicator.style.opacity = 1.0;
+ ext.indicator.style.display = 'block';
+ ext.indicatorTimeout = setTimeout(function() {
+ ext.indicator.style['-webkit-transition'] = null;
+ ext.indicator.style.opacity = 0.0;
+ ext.indicatorFadeTimeout = setTimeout(function() {
+ ext.indicator.style.display = null;
+ }, 500);
+ }, 1000);
+ }
+ },
+ flashIndicator: function() {
+ clearTimeout(ext.indicatorFlashTimeout);
+ if ( ext.indicator ) {
+ ext.indicatorInner.className = 'red';
+ ext.indicatorFlashTimeout = setTimeout(function() {
+ ext.indicatorInner.className = '';
+ }, 200);
+ }
+ },
+ selectedTextEqualsNextSearchString: function() {
+ var s = window.getSelection();
+ return s.rangeCount && ext.trim(String(s).toLowerCase()) == ext.trim(ext.nextSearchString.toLowerCase());
+ },
+ handleNonAlphaKeys: function(e) {
+ e.cmdKey = e.metaKey && !e.ctrlKey;
+ e.character = String.fromCharCode(e.keyCode);
- // handle typeable keypresses
- window.addEventListener('keypress', function(e) {
- e.cmdKey = e.metaKey && !e.ctrlKey;
- e.character = String.fromCharCode(e.keyCode);
- // if it was a typeable character, Cmd key wasn't down, and a field doesn't have focus, and char isn't return or space
- if ( e.keyCode && !focusedElement() && !e.cmdKey && e.keyCode != 13 && e.character != ' ' ) {
-
- // append char
- searchString += e.character;
- nextSearchString = searchString;
-
- // postpone clearing
- clearTimeout(keyupTimeout);
- if ( indicator ) {
- indicatorInner.innerHTML = searchString;
- indicator.style['-webkit-transition'] = 'none';
- indicator.style.opacity = 1.0;
+ // handle esc in fields (blur)
+ if ( e.keyCode == 27 ) {
+ ext.displayInIndicator('');
+ if ( ext.focusedElement() || ext.selectedTextEqualsNextSearchString() ) {
+ document.activeElement.blur(); // FIXME: this isn't deselecting selection
+ } else {
+ ext.flashIndicator();
+ }
+ return;
}
-
- keyupTimeout = setTimeout(function() {
- searchString = '';
+
+ // if cmd-g and we have go to next
+ var s = window.getSelection();
+ if ( e.character == 'G' && e.cmdKey && ext.selectedTextEqualsNextSearchString() ) {
+ window.find(ext.nextSearchString, false, e.shiftKey, true, false, true, false);
+
+ // make sure we're not now IN indicator div, if so find again
+ if ( ext.trim(s.anchorNode.parentNode.id) == ext.trim(ext.indicatorInner.id) ) {
+ window.find(ext.nextSearchString, false, e.shiftKey, true, false, true, false);
+ }
+
+ ext.focusSelectedLink(ext.nextSearchString);
+ ext.displayInIndicator(ext.nextSearchString +' (⌘G)');
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ }
+ },
+ handleAlphaKeys: function(e) {
+ e.cmdKey = e.metaKey && !e.ctrlKey;
+ e.character = String.fromCharCode(e.keyCode);
+ // if it was a typeable character, Cmd key wasn't down, and a field doesn't have focus, and char isn't return or space
+ if ( e.keyCode && !ext.focusedElement() && !e.cmdKey && e.character != ' ' ) {
- // indicator
- if ( indicator ) {
- indicator.style['-webkit-transition'] = null;
- indicator.style.opacity = 0.0;
+ if ( e.keyCode == 13 && ext.nextSearchString ) { // return key but no link; flash
+ ext.displayInIndicator(ext.nextSearchString + '');
+ ext.flashIndicator();
+ } else {
+ // append char
+ ext.searchString += e.character;
+ ext.nextSearchString = ext.searchString;
+
+ // clear selection and find again
+ window.getSelection().removeAllRanges();
+ window.find(ext.searchString, false, false, true, false, true, true);
+
+ // focus the link so return key follows
+ ext.focusSelectedLink(ext.nextSearchString);
+
+ ext.displayInIndicator(ext.nextSearchString);
}
- }, 1000);
-
- // clear selection and find again
- window.getSelection().removeAllRanges();
- window.find(searchString, false, false, true, false, true, true);
-
- // focus the link so return key follows
- focusSelectedLink(nextSearchString);
+
+ // postpone clearing
+ clearTimeout(ext.keyupTimeout);
+ ext.keyupTimeout = setTimeout(function() {
+ ext.searchString = '';
+ }, 1000);
+ }
+ },
+ init: function() {
+
+ // add indicator div to page
+ if ( document.readyState == 'complete' )
+ ext.createIndicator();
+ else
+ window.addEventListener('load', function() {
+ ext.createIndicator();
+ });
+
+ // handle command-g & esc
+ window.addEventListener('keydown', function(e) {
+ ext.handleNonAlphaKeys(e);
+ });
+
+ // handle typeable keypresses
+ window.addEventListener('keypress', function(e) {
+ ext.handleAlphaKeys(e);
+ });
}
- });
+ };
+ ext.init();
})();
View
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='golgkplpmkdbcieogdagppemcpgmncin'>
- <updatecheck codebase='http://github.com/dbergey/type_to_navigate_chrome/raw/master/type_to_navigate.crx' version='0.27' />
+ <updatecheck codebase='http://github.com/dbergey/type_to_navigate_chrome/raw/master/type_to_navigate.crx' version='0.28' />
</app>
</gupdate>

0 comments on commit d68b4d5

Please sign in to comment.