Google Date Keeper 0.4.3

By littlespark Last update Nov 13, 2009 — Installed 2,441 times. Daily Installs: 1, 0, 0, 5, 1, 0, 1, 4, 114, 29, 34, 11, 7, 4, 6, 5, 4, 11, 11, 5, 4, 3, 113, 27, 42, 34, 38, 22, 14, 19, 14, 12

There are 16 previous versions of this script.

/*
	Google Date Keeper
	First release: 17 Dec 2008, v0.1.0 (Greasemonkey version)
	
	UPDATES 
	* * * * * * 	
	[ 13 Nov 2009 ]
	v.0.4.3 - [ * ] More complete testing of parameters when setting date selector value
	v.0.4.2 - [ * ] Change regular expression for responseText.
	v.0.4.1 - [ * ] Change addEventListener to target document instead of date selector itself
	v.0.4.0 - [ * ] Test page with language input node instead (neccessary for certain sites). Script will run only if test pass.
	v.0.3.9 - [ + ] Redirect sites with # to search?
	v.0.3.8 - [ * ] Change @include from search* to *
	v.0.3.7 - [ - ] Remove checking of url in script. Use only @include.
	
	[ 30 Oct 2009 ]
	v.0.3.6 - [ + ] Make adjustment to synchronize with the new internal google date options
	v.0.3.5 - [ - ] Remove feature that show date info when date option is at "anytime"
	v.0.3.4 - [ * ] Auto version checker feature: Fix bug that only check when date option is not set at "anytime"
	
*/

// ==UserScript==
// @name           Google Date Keeper
// @namespace      http://userscripts.org/scripts/show/38800
// @description    Date dropdowns disappearing at google search? Try this persistent date selector! Works for all countries and languages + Has extended date features! Easy, intuitive and flexible.
// @include        http://www.google.*/*
// @exclude
// @svc:version    [0.4.3]
// ==/UserScript==

var gdk = {
	dateSelectNode: null, submitNode: null, language: null, timer: null, keypressed: null,
	init: function () {
		gdk.redirect();
		if (top != self) return;
		
		// clean up unused GM_values
		try { GM_deleteValue('showDateInfoForAllOption'); } catch (err) { }
		
		gdk.language = document.evaluate("//input[@name='hl']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0).value;
		if (!gdk.language) return;
		
		gdk.testDateSelector();
		gdk.common.init();
	},
	redirect: function () {
		var re = /(https?:\/\/[^\/]+?\/)(#)/;
		if (re.test(top.location.href)) top.location.href = top.location.href.replace(re,'$1search?').replace('fp=','');
	},
	refreshDateSelectNode: function () {
		gdk.dateSelectNode = document.evaluate("//select[@name='as_qdr']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
	},
	testDateSelector: function () {
		gdk.refreshDateSelectNode();
		// if date selector already exist
		if (gdk.dateSelectNode) {
			// check page language with locally stored value
			// If they don't match, retrieve and store date selector locally else do nothing
			if (GM_getValue('language') != gdk.language) gdk.buildDateSelector.storeLocalValues(gdk.dateSelectNode.innerHTML);
			return;
		}
		
		gdk.submitNode = document.evaluate("//input[@name='btnG']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
		
		// check page language with locally stored value.  If they match, build date selector
		if (GM_getValue('language') == gdk.language && GM_getValue('dateselector') != "") { 
			gdk.buildDateSelector.start(GM_getValue('dateselector'));
			return;
		}
		
		// build date selector from scatch
		gdk.xmlHttpRequest.start();
	},
	xmlHttpRequest: {
		start: function () {
			GM_xmlhttpRequest({
				method: 'GET', url: location.href.replace(/search\?/i, 'advanced_search?'), 
				onload: function (res) {
					var responseText = gdk.xmlHttpRequest.processResponseText(res.responseText);
					if (responseText != '') {
						gdk.buildDateSelector.start(responseText);
						gdk.buildDateSelector.storeLocalValues(responseText);
					}
				}
			});
		},
		processResponseText: function (responseText) {
			var re = /name=.?as_qdr.?>(.+?)<\/select>/;
			if (re.test(responseText)) {
				return (/name=.?as_qdr.?>(.+?)<\/select>/.exec(responseText)[1]);
			} else {
				return '';
			}
		},
	},
	buildDateSelector: {
		start: function (htmlText) {
			// CHECK IF USER'S SELECTION IS FOUND IN THE DROPDOWN
			var dateValueAS;
			
			var flag = false;
			var re = /as_qdr=([a-zA-Z]+)(\d*)&|$/;
			if (re.test(location.href)) {
				dateValueAS = re.exec(location.href)[1];
				if (re.exec(location.href)[2] == '') flag = true;
			}
				
			// ... when user's selection isn't found in the dropdown
			if (flag == false) {
				gdk.dateGoogle = document.evaluate("//input[@name='tbs']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
				re = /qdr:(.)(\d*)$/;
				if (gdk.dateGoogle && re.test(gdk.dateGoogle.value)) {
					if (re.exec(gdk.dateGoogle.value)[2] == '') {
						dateValueAS = re.exec(gdk.dateGoogle.value)[1];
						flag = true;
					} else {
						var target = document.getElementById('prs');
						var dateValueTB = re.exec(gdk.dateGoogle.value)[1];
						var dateText = /<b>(.+)<\/b>/.exec(target.innerHTML)[1].toLowerCase();
						htmlText = htmlText + '<option selected value="' + dateValueTB + '">' + dateText + '</option>';
					}
				} else {
					dateValueAS = 'all';
					flag = true;
				}
			}
			// build date selector
			var newTag = document.createElement('select');
			newTag.name = "as_qdr";
			newTag.setAttribute('style', 'margin-left: 3px; margin-right: 3px');
			newTag.innerHTML = htmlText;
			gdk.submitNode.parentNode.insertBefore(newTag, gdk.submitNode);
			
			// refresh node
			gdk.refreshDateSelectNode();
			
			// ... when user's selection is found in the drop down
			if (flag == true) if (dateValueAS) gdk.dateSelectNode.value = dateValueAS;
			
			// remove duplicate input name=as_qdr
			var node = document.evaluate("//input[@name='as_qdr']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
			if (node) node.parentNode.removeChild(node);
	},
		storeLocalValues: function (htmlText) {	
			GM_setValue('language', gdk.language);
			GM_setValue('dateselector', htmlText);
		},
	},
	common: {
		init: function () {
			// add listeners
			if (document.forms[0]) document.forms[0].addEventListener("submit", gdk.common.events.submit, true);
			
			document.addEventListener('keydown', function (e) {
				if (e.target.tagName == 'SELECT' && e.target.name == 'as_qdr') gdk.common.events.keyDownEvent(e);
			}, false);
			
			// THE "SCRIPT VERSION CHECKER"
			GM_registerMenuCommand("Google Date Keeper (Check Latest Version)", SVC.versionInfo.manualChecking);
			SVC.versionInfo.autoChecking();
		},	
		events: {
			submit: function () {				
				// when there is internal google date options
				gdk.dateGoogle = document.evaluate("//input[@name='tbs']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
				
				if (gdk.dateGoogle) {
					gdk.refreshDateSelectNode();
					var value = gdk.dateSelectNode.options[gdk.dateSelectNode.selectedIndex].value;
					value == "all" ? gdk.dateGoogle.value = "" : gdk.dateGoogle.value = "qdr:" + value;
				}
			},
			keyDownEvent: function (e) {
				if ((e.which >=48 && e.which <=57) || (e.which >=96 && e.which <=105)) {
					if (e.which >=48 && e.which <=57) // main number keys
						gdk.keypressed ? gdk.keypressed = gdk.keypressed + String.fromCharCode(e.which)
							: gdk.keypressed = String.fromCharCode(e.which);
					
					if (e.which >=96 && e.which <=105) // num pad keys
						gdk.keypressed ? gdk.keypressed = gdk.keypressed + String.fromCharCode(e.which-48)
							: gdk.keypressed = String.fromCharCode(e.which-48);
					
					gdk.timer = setTimeout(function () { gdk.common.timerReachedAction (e); }, 1000);
				} else {
					window.clearTimeout(gdk.timer);
					gdk.keypressed = null;
				}
			},
		},
		timerReachedAction: function (e) {
				if (e.target.value == 'all') return;
				
				// update value in date select node
				gdk.dateSelectNode.options[gdk.dateSelectNode.selectedIndex].value = e.target.value.substring(0,1) + /[1-9]\d*/.exec(gdk.keypressed);
				
				// when there are internal google date options, update value in google date node
				gdk.dateGoogle = document.evaluate("//input[@name='tbs']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
				if (gdk.dateGoogle) gdk.dateGoogle.value = "qdr:" + gdk.dateSelectNode.options[gdk.dateSelectNode.selectedIndex].value
				
				gdk.keypressed = null;
				document.forms[0].submit();
			},
	},
};

// SCRIPT VERSION CHECKER
// VERSION 0.2.0
var SVC = {
	currentVersion: "0.4.3",
	scriptName: "Google Date Keeper",
	scriptNum: 38800,
	
	// GLOBAL SETTINGS
	currentDate: null, userRequestCheck: null, timer: null,
	
	init: function () {
		SVC.currentDate = new Date();
		var cv = parseInt(/[1-9][\d]*/.exec(SVC.currentVersion.replace(/\D/g, "")));

		// INITIALIZE LOCAL VALUES (FOR FIRST-TIME USE)
		if (!GM_getValue("latest")) GM_setValue("latest", cv );
		if (!GM_getValue("notified")) GM_setValue("notified", false);
		if (!GM_getValue("lastChecked")) GM_setValue("lastChecked", (SVC.currentDate.getTime() - 1000*60*60*25) + "");
		
		// UPDATE LOCAL VALUES (FOR FIRST-TIME USE AFTER REINSTALL NEWER VERSION)
		if (GM_getValue("latest") < cv) {
			GM_setValue("latest", cv);
			GM_setValue("notified", false);
			GM_setValue("lastChecked", SVC.currentDate.getTime() + "");
		}
	},
	verify: function () {
		SVC.userRequestCheck = false;
		var sp = SVC.currentDate.getTime() - parseInt(GM_getValue("lastChecked"));
		
		// CHECK SOURCE IF USER HAS BEEN NOTIFIED OF AN UPDATED VERSION BEFORE AND 14 DAYS HAVE PASSED
		if (GM_getValue("notified") && (sp / (1000*60*60*24) > 14)) SVC.getInfo();
			
		// CHECK SOURCE FOR THE LATEST VERSION IF ONE DAY HAS PASSED SINCE LAST CHECKED
		if (!GM_getValue("notified") && ( sp / (1000*60*60*24) > 1 )) SVC.getInfo();
	},
	getInfo: function () {	
		var uso = 'http://userscripts.org';
		function retrieve(url, re, count) {
			SVC.xhr.get(url, function (status, text) {
				window.clearTimeout(SVC.timer);
				if (status == 404 && SVC.userRequestCheck) SVC.manualErrorMsg();
				if (status == 200) {
					if (re.test(text)) var uv = re.exec(text)[1];
					if (uv) SVC.compare(uv);
					if (!uv && count == 1) {
						retrieve(uso + '/scripts/show/' + SVC.scriptNum, /<h1.+>.+\s([^\s]+)<\/h1>/, 2);
					} else if (!uv && SVC.userRequestCheck) {
						SVC.manualErrorMsg();
					}
				}
			});
			SVC.timer = setTimeout(function () { 
				if (count == 1) retrieve(uso + '/scripts/show/' + SVC.scriptNum, /<h1.+>.+\s([^\s]+)<\/h1>/, 2);
				if (count == 2) SVC.manualErrorMsg();
			}, 2000);
		};
		retrieve(uso + '/scripts/source/' + SVC.scriptNum + '.meta.js', /@svc:version[\s]*\[(.+)\]/, 1);
	},
	xhr: {
		get: function (url, process) {
			GM_xmlhttpRequest({
				method: 'GET',
				url: url,
				onload: function (res) { process(res.status, res.responseText); },
			});
		},
	},
	compare: function (version) {
			
			var updatedVersionInt = parseInt(/[1-9][\d]*/.exec(version.replace(/\D/g, "")));
			
			// DO NOTHING IF NO CHANGE IN VERSIONS
			if (updatedVersionInt <= GM_getValue("latest")) {
				if (SVC.userRequestCheck) alert('Auto-check completed!\n---------------------------\n\nYou are using the latest greasemonkey script \n\n~ ' + SVC.scriptName + ' ~ version ' + SVC.currentVersion + '.\n\n  ');
				return;
			}
			
			GM_setValue("notified", true);
			GM_setValue("lastChecked", SVC.currentDate.getTime() + "");
			
			// NOTIFY USER
			if (SVC.userRequestCheck) {
			
				var reply = confirm('Auto-check completed!\n---------------------------\n\nThe Greasemonkey Script ~ ' + SVC.scriptName + ' ~ has recently been updated to v.' + version + '. \n\nYou are currently using version ' + SVC.currentVersion + '.\nWould you like to visit the download page at userscript.org now?\n\n  ');
				
				if (reply) GM_openInTab("http://userscripts.org/scripts/show/" + SVC.scriptNum);
				
			} else {
			
				var reply = confirm('Latest news for Greasemonkey Scripts!\n-----------------------------------------------\n\nThe Greasemonkey Script ~ ' + SVC.scriptName + ' ~ has recently been updated to v.' + version + '. \n\nYour current working version is ' + SVC.currentVersion + '.\nWould you like to visit the download page at userscript.org now?\n\n  ');
				
				if (reply) GM_openInTab("http://userscripts.org/scripts/show/" + SVC.scriptNum);
			
			}
		},
	versionInfo: {
		autoChecking: function () {
			SVC.init();
			SVC.verify();
		},
		manualChecking: function () {
			SVC.userRequestCheck = true;
			SVC.getInfo();
		},
	},
	manualErrorMsg: function () {
		var reply = confirm('Alert!\n-------\n\nAuto-checking for the latest version of the Greasemonkey Script ~ ' + SVC.scriptName + ' ~ has not been successful.\n\nYou may wish to try again later or visit the download page to check manually. For your information, your current working version is ' + SVC.currentVersion + '. \n\nWould you like to visit the download page at userscript.org now?\n\n  ');
		if (reply) GM_openInTab("http://userscripts.org/scripts/show/" + SVC.scriptNum);
	},
};

gdk.init();