del.icio.us Navigator

By SatishChandra Last update Sep 27, 2008 — Installed 168 times.
// ==UserScript==
// @name           del.icio.us Navigator
// @namespace      del.icio.us_Navigator
// @description    Make del.icio.us page more accessible
// @include        http://delicious.com/*
// ==/UserScript==

/*
 * Help
 * -----
 * 
 * j - Goto next link
 * k - Goto prev link
 *
 * n - Goto next tag
 * m - Goto prev tag
 *
 * s - Open search dialog
 *
 * esc - Reset j/k, n/m positions to top
 * esc(inside search box) - Close search dialog
 */

//All global variables go in this.
var globals = {};

function getTaggedLinks() {
	var links = document.getElementsByTagName("a");
	var taggedLinks = [];
	for (var i = 0; i < links.length; i++) {
		if (links[i].className === "taggedlink") {
			taggedLinks[taggedLinks.length] = links[i];
		}
	}
	return taggedLinks;
}

function getTags() {
	//If user is logged in tags are in "ruser-tags", else in "user-tags"
    var tagContainer = document.getElementById("ruser-tags") || document.getElementById("user-tags");
	if (tagContainer) {
		var tags = tagContainer.getElementsByTagName("a");
		return tags;
	}
	return [];
}

function preProcess() {
    globals.taggedLinks = getTaggedLinks();
    globals.currentLink = -1;
    
    globals.tags = getTags();
    globals.currentTag = -1;
    
    //GM_log("No of taggedLinks: "+globals.taggedLinks.length);
    //GM_log("No of tags: "+globals.tags.length);
    
    //Create a map of tag names. tagName -> tag Link
    var map = {};
    for(var i=0;i<globals.tags.length;i++) {
		//GM_log(globals.tags[i].title);
    	map[globals.tags[i].title] = globals.tags[i];
    }
    
	globals.tagMap = map;
}

function isInputTarget(e) {
	var target = e.target || e.srcElement;
	if (target && target.nodeName) {
		var targetNodeName = target.nodeName.toLowerCase();
		if (targetNodeName == "textarea" || targetNodeName == "select" ||
			(targetNodeName == "input" && target.type &&
				(target.type.toLowerCase() == "text" ||
					 target.type.toLowerCase() == "password"))
						 )  {
			return true;
		}
	}
	return false;
}

function keyPressEvent(event){
	if(isInputTarget(event)) {
		return;
	}

	//If a key was pressed with these keys, ignore it
	if (event.altKey || event.ctrlKey || event.metaKey) {
		return false;
	}

    var keyNum = event.charCode ? event.charCode : event.keyCode;
	//var letter = String.fromCharCode(keyNum).toLowerCase();
    switch (keyNum) {
        case 27: //Escape
            reset();
            break;
        case 106: // j
            selectNextLink();
            break;
        case 107: // k
            selectPrevLink();
            break;
        case 110: // n
            selectNextTag();
            break;
        case 109: // m
            selectPrevTag();
            break;
		case 115: // s
			TagSuggest.init();
			break;
            
        default:
    }
}

function reset() {
    if (globals.currentLink != -1) {
        globals.taggedLinks[globals.currentLink].blur();
        clearBorder(globals.taggedLinks[globals.currentLink]);
        globals.currentLink = -1;
    }
    
    if (globals.currentTag != -1) {
        globals.tags[globals.currentTag].blur();
		clearBorder(globals.tags[globals.currentTag]);
        globals.currentTag = -1;
    }
}

function selectNextLink() {
    if (globals.currentLink < globals.taggedLinks.length - 1) {	    
        globals.currentLink++;
        globals.taggedLinks[globals.currentLink].focus();
        setBorder(globals.taggedLinks[globals.currentLink]);
        if(globals.currentLink != 0) {
	        clearBorder(globals.taggedLinks[globals.currentLink - 1]);
	    }
    }
}

function selectPrevLink() {
    if (globals.currentLink > 0) {
	    clearBorder(globals.taggedLinks[globals.currentLink]);
        globals.currentLink--;
        globals.taggedLinks[globals.currentLink].focus();
        setBorder(globals.taggedLinks[globals.currentLink]);
    }
}

function selectNextTag() {
    if (globals.currentTag < globals.tags.length - 1) {    	
        globals.currentTag++;
        globals.tags[globals.currentTag].focus();
        setBorder(globals.tags[globals.currentTag].firstChild);
        if(globals.currentTag != 0) {
	        clearBorder(globals.tags[globals.currentTag - 1].firstChild);
	    }
    }
}

function selectPrevTag() {
    if (globals.currentTag > 0) {
	    clearBorder(globals.tags[globals.currentTag].firstChild);
        globals.currentTag--;
        globals.tags[globals.currentTag].focus();
        setBorder(globals.tags[globals.currentTag].firstChild);
    }
}

function setBorder(element) {
	if(element && element.style) {
		element.style.backgroundColor="#efefef";
	}
}

function clearBorder(element) {
	if(element && element.style) {
		element.style.backgroundColor="#fff";
	}
}

/*	TagSuggest Start	*/
// Show a text box and show matching tags when text is typed.
// Tag matching is by mathcing typed text with beginning of each tag.
function TagSuggest() {}
	
TagSuggest.init = function() {	
	globals.tagSearch.style.display="inline";
	Util.centerElement(globals.tagSearch);
	globals.tagSearchInput.focus();
}

TagSuggest.showTags = function(event) {
	var keyNum = event.charCode ? event.charCode : event.keyCode;
	if(keyNum == 27) { //Esc
		TagSuggest.reset();
		return;
	}
	
	if(keyNum != 8 && keyNum != 46) {//Backspace and Delete
		//Ignore other control keys
		if (keyNum < 32 || (keyNum >= 33 && keyNum < 46) || (keyNum >= 112 && keyNum <= 123)) {
			return;
		}
	}
	
	//Clear previous matches.
	globals.tagListDisplay.innerHTML = "";
	
	var text = globals.tagSearchInput.value;
	if(text.trim() == "") {
		globals.tagListDisplay.style.visibility = "hidden";
		return;
	}
	
	var showList = [];
	
	for(prop in globals.tagMap) {
		var regex = new RegExp("^" + text, "i");
		if(prop.match(regex)) {
			showList[showList.length] = prop; 
		}
	}

	if(showList.length == 0) {
		globals.tagListDisplay.style.visibility = "hidden";
		return;
	}
	
	//Show new mathces.
	for(var i=0; i<showList.length; i++) {
		var ele = document.createElement("div");
		ele.innerHTML = showList[i];
		globals.tagListDisplay.appendChild(ele);
	}
	
	TagSuggest.styleTagList();
	
	globals.tagPos = -1;
}

TagSuggest.styleTagList = function() {
	var pos = Util.getPosition(globals.tagSearchInput);

	//Use this if element style is fixed
	//globals.tagListDisplay.style.left = pos.left + "px"; 
	//globals.tagListDisplay.style.top = pos.top + globals.tagSearchInput.offsetHeight + "px";
	
	//Use this if element style is absolute 
	globals.tagListDisplay.style.left = globals.tagSearch.offsetWidth - globals.tagSearchInput.offsetWidth -6 + "px";
	globals.tagListDisplay.style.top = globals.tagSearchInput.offsetHeight + 3 + "px";

	globals.tagListDisplay.style.width = globals.tagSearchInput.offsetWidth + "px";
	globals.tagListDisplay.style.visibility = "visible";

	//For each tag element, add mouse over/down listeners
	var childNodes = globals.tagListDisplay.childNodes;
	for(var i=0; i<childNodes.length; i++) {
		childNodes[i].addEventListener('mouseover', TagSuggest.mouseover, true);
		childNodes[i].addEventListener('mousedown', TagSuggest.mousedown, true);
	}
}

TagSuggest.reset = function() {
	globals.tagSearch.style.display="none";
	globals.tagSearchInput.blur();
	globals.tagSearchInput.value="";
}

TagSuggest.navigateList = function(event) {
	var keyNum = event.charCode ? event.charCode : event.keyCode;
	
	switch(keyNum) {
		case 38: //Up arrow
			TagSuggest.previousSuggestion();
			break;
		case 40: //Down arrow
			TagSuggest.nextSuggestion();
			break;
		case 13: //Enter
			TagSuggest.hideSuggestions();
			break;
	}
}

TagSuggest.previousSuggestion = function() {
	var childNodes = globals.tagListDisplay.childNodes;
	if (globals.tagPos > 0) {
		TagSuggest.highlightNode(childNodes[--globals.tagPos]);
	}	
}

TagSuggest.nextSuggestion = function() {
	var childNodes = globals.tagListDisplay.childNodes;
	if (globals.tagPos < childNodes.length - 1) {
		TagSuggest.highlightNode(childNodes[++globals.tagPos]);
	}
}

TagSuggest.hideSuggestions = function() {
	var value = globals.tagListDisplay.childNodes[globals.tagPos].innerHTML;
	TagSuggest.openTag(value);
}

TagSuggest.openTag = function(tagName) {
	globals.tagSearchInput.value = tagName; 
	globals.tagListDisplay.innerHTML = "";
	globals.tagListDisplay.style.visibility = "hidden";
	location.href = globals.tagMap[tagName];
}

TagSuggest.highlightNode = function(node) {
	var childNodes = globals.tagListDisplay.childNodes;
	for(var i=0; i<childNodes.length; i++) {
		if(childNodes[i] == node) {
			childNodes[i].style.backgroundColor = "#0045c3";
			childNodes[i].style.color = "#fff";
		} else {
			childNodes[i].style.backgroundColor = "";
			childNodes[i].style.color = "";
		}
	}
}

TagSuggest.mouseover = function(event) {
	var target = event.target;
	TagSuggest.highlightNode(target);
}

TagSuggest.mousedown = function(event) {
	var target = event.target;
	TagSuggest.openTag(target.innerHTML);
}
/*	TagSuggest End	*/

/*	Util Start	*/
function Util() {};

Util.getPosition = function (ele) {
	var top = 0;
	var left = 0;
	do {
		if (ele.offsetParent) {
			top += ele.offsetTop;
			left += ele.offsetLeft;
		}
	} while(ele = ele.offsetParent)	
	return {"top" : top, "left": left};
}

Util.centerElement = function(ele) {
	
	var viewportwidth = window.innerWidth;
    var viewportheight = window.innerHeight;
    
    var eleHeight = ele.offsetHeight;
    var eleWidth = ele.offsetWidth;

    //Position element 1/10th distance from top and half from left
    var topPos = (viewportheight - eleHeight)/10;
    var leftPos = (viewportwidth - eleWidth)/2;
    
    ele.style.top = topPos + "px"; 
    ele.style.left = leftPos + "px";
}

Util.printObj = function(obj) {
	var msg = "";
	for(var prop in obj) {
		msg += prop + " : " + obj[prop] + "<br>"; 
	}
	document.getElementById("log").innerHTML = msg;
}

Util.log = function(msg) {
	document.getElementById("log").innerHTML = document.getElementById("log").innerHTML + "<br>" + msg;
}
/*	Util End	*/

String.prototype.trim = function () {
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
}

function init() {
	//Insert the search element in the doc. It is absolutely positioned.
	var searchEle = document.createElement("div");
	searchEle.innerHTML =
		'<div id="tagSearch" style="' +
		'       background-color: #3274d0;' +
		'       color: #fff;' +
		'       display: none;' +
		'       padding: .3em 0.5em;' +
		'       position: absolute;' +
		'		z-index: 11000;' +
		'       ">' +
		'       Search for Tag: <input id="tagSearchInput" type="text" size=15/>' +
		'       <div id="tagListDisplay" style="' +
		'               border: 1px solid #000;' +
		'               background-color: #fff;' +
		'				color: #000;' +
		'               position: absolute;' +
		'               text-align: left;' +
		'               visibility: hidden;' +
		'               ">              ' +
		'       </div>' +
		'</div>';
	
	document.body.insertBefore(searchEle, document.body.firstChild);

	globals.tagSearch = document.getElementById("tagSearch");
	globals.tagListDisplay = document.getElementById("tagListDisplay");
	globals.tagSearchInput = document.getElementById("tagSearchInput");
	globals.tagPos = -1;

	//Add listners for text box. "showTags" for showing matching tags
	//and "navigateList" for keyboard navigation.
	var tagSearchInput = document.getElementById("tagSearchInput");
	tagSearchInput.addEventListener('keyup', TagSuggest.showTags, true);
	tagSearchInput.addEventListener('keypress', TagSuggest.navigateList, true);

	preProcess();
}


//Add Listeners
document.addEventListener('keypress', keyPressEvent, true);
document.addEventListener('load', init, true);