Focus Search

By Richard Gibson Last update Apr 21, 2011 — Installed 218 times.

There are 5 previous versions of this script.

// ==UserScript==
// @name           Focus Search
// @namespace      http://userscripts.org/people/336
// @description    Responds to Ctrl+/ by attempting to find and focus a page's search box.
// @source         http://userscripts.org/scripts/show/54674
// @identifier     http://userscripts.org/scripts/source/54674.user.js
// @version        0.1.4
// @date           2011-04-21
// @creator        Richard Gibson <@gmail.com>
// @include        *
// ==/UserScript==
// 
// **COPYRIGHT NOTICE**
// 
// I, Richard Gibson, hereby establish my original authorship of this
// work, and announce its release into the public domain.  I claim no
// exclusive copyrights to it, and will neither pursue myself (nor
// condone pursuit by others of) punishment, retribution, or forced
// payment for its full or partial reproduction in any form.
// 
// That being said, I would like to receive credit for this work
// whenever it, or any part thereof, is reproduced or incorporated into
// another creation; and would also like compensation whenever revenue
// is derived from such reproduction or inclusion.  At the very least,
// please let me know if you find this work useful or enjoyable, and
// contact me with any comments or criticisms regarding it.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// 
// **END COPYRIGHT NOTICE**
//
//
// Changelog:
// 0.1.4 (2011-04-21)
// 	Updated: basic support for Google Chrome
// 0.1.3 (2011-02-21)
// 	Fixed: Firefox/Windows security bug
// 0.1.2 (2011-02-19)
// 	Updated: update checking with meta.js
// 0.1.1 (2010-07-03)
// 	improved: expanded matching id fields
// 0.1 (2009-07-28)
// 	original release
//
// -------------------------------------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts, select this script,
// and click Uninstall.
//
// -------------------------------------------------------------------------------------------------

(function(){

// AUTOMATIC UPDATES -------------------------------------------------------------------------------
var SCRIPT = {
	 name: "Focus Search"
	,namespace: "http://userscripts.org/people/336"
	,description: "Responds to Ctrl+/ by attempting to find and focus a page's search box."
	,source: "http://userscripts.org"      // script homepage/description URL
			+ "/scripts/show/54674"
	,identifier: "http://userscripts.org"  // script URL
			+ "/scripts/source/54674.user.js"
	,meta: "http://userscripts.org"        // metadata URL
			+ "/scripts/source/54674.meta.js"
	,version: "0.1.4"                      // version
	,date: "2011-04-21"                    // update date
};
var UPDATE = {
	 SCRIPT: SCRIPT
	,defaults: {checkDays: 3, version: SCRIPT.version, date: SCRIPT.date, name: SCRIPT.name,
			lastCheck: typeof(GM_xmlhttpRequest)!='undefined' ? 0 : (new Date()).getTime()}
	,getValue: (typeof(GM_getValue)!='undefined' && GM_getValue)
	,setValue: (typeof(GM_setValue)!='undefined' && GM_setValue)
	,HttpRequest: (typeof(GM_xmlhttpRequest)!='undefined' && GM_xmlhttpRequest) || function(){}
	,init: function() {
		if (this.setValue) {
			this.setValue('_UPDATE_setValue', true);
			if(!this.getValue('_UPDATE_setValue')){ this.setValue = false; }
		}
		if (!this.setValue) {
			this.getValue = this.setValue = function(n,v){return v;};
		}
	 	for (var name in this.defaults) {
	 		if(name in this){ delete this.defaults[name]; }
	 		else{ this[name] = this.getValue('_UPDATE_' + name, this.defaults[name]); }
	 	}
	 	for (var p in {checkDays:0, lastCheck:0}) { delete this.defaults[p]; }
	 	this.init = new Function('return "already done"; //arguments.callee.replaced.apply(this, arguments);');
		this.init.replaced = arguments.callee;
	}
	,check: function(fnOnNewer, fnIsNewer, blnForce) {
		this.init();
		var interval = Math.max(parseFloat(this.checkDays) * 24 * 60 * 60 * 1000, 0) || Infinity;
		var diff = (new Date()) - parseInt(this.lastCheck,10);
		if(!blnForce && !this.isNewer(this, this.SCRIPT, fnIsNewer) && !(diff > interval)){ return false; }
		if (blnForce || (diff > interval)) {
			var t = this;
			return this.HttpRequest({method: 'GET', url: this.SCRIPT.meta || this.SCRIPT.identifier, onload: function(r){
				t.setValue('_UPDATE_' + 'lastCheck', t.lastCheck = '' + (new Date()).getTime());
				t.parse(r.responseText, [fnOnNewer, fnIsNewer, false]);
			}});
		}
		try{ fnOnNewer(this, this.SCRIPT); }catch(x){}
	}
	,parse: function(strResponse, arrCheckArgs) {
		var re = /\/\/\s*(?:@(\S+)\s+(.*?)\s*(?:$|\n)|(==\/UserScript==))/gm, match = true, name;
		while (match && (match = re.exec(strResponse))) {
			if(match[3]){ match = null; continue; }
			name = match[1];
			if(name in this.defaults){ this.setValue('_UPDATE_' + name, this[name] = match[2]); }
		}
		this.check.apply(this, arrCheckArgs || []);
	}
	,isNewer: function(objUpdate, objScript, fnIsNewer) {
		if(!objUpdate){ objUpdate = this; }
		if(!objScript || (objUpdate.date > objScript.date)){ return true; }
		try {
			return fnIsNewer(objUpdate, objScript);
		}
		catch (x) {
			return (!(objUpdate.date < objScript.date) && (objUpdate.version != objScript.version));
		}
	}
};

// BEGIN SCRIPT ------------------------------------------------------------------------------------
// constants
var NAME_RE = /query|search|^for|^q$/i;
var ID_RE = /query|search|^q$/i;
var UNSAFE = ((typeof unsafeWindow)=='object' && unsafeWindow);
	if (!UNSAFE || UNSAFE === (function(){return this;})()) {
		UNSAFE = document.createElement('a');
		UNSAFE.setAttribute('onclick', 'return (function(){return this;})();');
		UNSAFE = UNSAFE.onclick();
	}

// globals
var gobjLastFocused = null;

// MAIN --------------------------------------------------------------------------------------------
function focusSearch () {
	var arrInputs = document.getElementsByTagName('input');
	var arrCandidates = [];
	var idxNext = 0;	// next potential index (if we've already matched)
	var objMatch = null;
	for (var i = 0; i < arrInputs.length; i++) {
		if (arrInputs[i].type == 'text') {
			arrCandidates.push(arrInputs[i]);
		}
	}

	for (var i = 0, current; i < arrCandidates.length; i++) {
		current = arrCandidates[i];
		if ((!objMatch || idxNext) && (NAME_RE.test(current.name) || ID_RE.test(current.id))) {
			// first match (or first after the previous)
			objMatch = current;
			if (!gobjLastFocused || idxNext){ break; }	// we just replaced the previous match (if any)
		}
		if (!idxNext && current.isSameNode(gobjLastFocused)){ idxNext = i + 1; }	// found the previous
	}
	if (!objMatch) {
		// use the first candidate following the previous match, looping around if necessary
		objMatch = arrCandidates[idxNext % arrCandidates.length];
	}

	if (objMatch) {
		gobjLastFocused = objMatch;
		objMatch.focus();
		objMatch.select();
	}
}

function listener (e) {
	if (e.ctrlKey && (e.which || e.keyCode) == 191) { focusSearch(); }
}

// show new version information
function showUpdate(objUpdate, objScript) {
	if(UNSAFE.self !== UNSAFE.top){ return; }
	if(!arguments.length){ return UPDATE.check(arguments.callee); }
	var title = objUpdate.name + ' ' + objUpdate.version + ', released ' + objUpdate.date;
	var style = [
		 'position:absolute; position:fixed; z-index:9999;'
		,'bottom:0; right:0;'
		,'border:1px solid black; padding:2px 2px 2px 0.5ex;'
		,'background:#dddddd; font-weight:bold; font-size:small;'
	].join(' ');
	document.body.appendChild($E('div', {title: title, style: style}
		,$E('a', {href: objScript.source, style: 'color:blue;'}, objScript.name + ' ')
		,$E('a', {href: objScript.identifier, style: 'color:red;'}, 'updated!')
		,$E('button', {onclick: 'return this.parentNode.parentNode.removeChild(this.parentNode) && false;',
				style: 'margin-left:1ex;font-size:50%;vertical-align:super;'}, '\u2573')
	));
}

// HELPERS -----------------------------------------------------------------------------------------
var $E = function createElement (strName, objAttributes, varContent /*, varContent, ...*/) {
	var el = document.createElement(strName);
	try{
		for (var attribute in objAttributes) {
			el.setAttribute(attribute, objAttributes[attribute]);
		}
	}catch(x){}
	if (arguments.length > 3 || (!/^(string|undefined)$/.test(typeof(varContent)) && !(varContent instanceof Array))) {
		varContent = Array.prototype.slice.call(arguments, 2);
	}
	if (varContent instanceof Array) {
		for (var L = varContent.length, i = 0, c; i < L; i++) {
			c = varContent[i];
			el.appendChild(c && typeof(c) == 'object' && 'parentNode' in c
					? c : document.createTextNode(c));
		}
	}
	else if (varContent) {
		el.innerHTML = varContent;
	}
	return el;
}

function attachEventListener (el, strEvent, fnListener, blnUseCapture) {
	try {
		el.addEventListener(strEvent, fnListener, blnUseCapture ? true : false);
	}
	catch (x) { try {
		el.attachEvent('on' + strEvent, fnListener);
	}
	catch (x) {} }
	return el;
}

// LISTENERS ---------------------------------------------------------------------------------------
attachEventListener(window, "keydown", listener, false);
if (document.readyState == 'complete') {
	showUpdate();
}
else {
	attachEventListener(window, 'load', function(){showUpdate();}, true);
}

})();