gibney.org
:
Technology
:
Javascript
:
Bookmarklets
:
Searcher
(Entry Nr. 188, by user 39 |
edit
)
/* Searcher finds all texts on document even those inside interactive elements as IMPUT, TEXTAREA etc. Written by Paul Geisler for Gibney Enterprises in 2006 */ var id="GIBNEY_ORG_SEARCHER_"; //ID für unique element namespace var searcherOverlayWindow; //our DIV-overlay var targetElements; //HTML-nodes of matches var targetText; //match content itself (see below) var IEgewurschtel=(navigator.appName=="Microsoft Internet Explorer"); //the usual suspects /* create a DIV-overlay */ function createDiv(id,posX,posY,content){ var menuDiv=document.createElement("div"); menuDiv.id=id; menuDiv.innerHTML=content; //setAttribute("style"... is MOZ only with collated string menuDiv.style.textAlign="left"; menuDiv.style.position="fixed"; menuDiv.style.backgroundColor="white"; menuDiv.style.border="1px solid black"; menuDiv.style.padding="0px"; if (IEgewurschtel){ menuDiv.style.position="absolute"; //IE has no "fixed" } menuDiv.style.top =posY+'px'; menuDiv.style.left=posX+'px'; document.getElementsByTagName('body').item(0).appendChild(menuDiv); return menuDiv; } /* call point for search Searches needle and put result-linls to element "Presenter" */ function search(){ var needle=document.getElementById(id+"Query").value var result=""; targetElements=new Array(); targetText=new Array(); if (needle.length>2) { //don't look for very short terms as this would return many results and slow things down //regexp for search term and some contextual content result=recurseSearch(document,new RegExp("(.{0,20})("+needle+")(.{0,20})","gi")); document.getElementById(id+"Presenter").innerHTML=result; } } /* recursive search the DOM below node for regexp reg returns links using mark() to mark the result on click */ function recurseSearch(node, reg){ var result=""; if (node.id==id+"TheSearcher") return ""; if (node.nodeType==3 || ("value" in node) ){ var text; if ("value" in node) //interaktive elements (INPUT; AREA) have no actual .data text=node.value; else text=node.data; if ( typeof (text)!="string" ) return ""; //just in case var hits; while(hits=reg.exec(text)){ var found='<pre style="font-family: arial sansserif;">'+hits[1]+"["+hits[2]+"]"+hits[3]+"</pre>"; var target=null; if ( "setAttribute" in node) //attachpoint for selection coarse target=node; else if ( "setAttribute" in node.parentNode) target=node.parentNode; if ("createRange" in document) //attachpoint for selection fine target=node; else if ( "selectionStart" in node) //MOZ target=node; else if ( "selectionStart" in node.parentNode) target=node.parentNode; if (target!=null){ //search term is markable in some way var pos=hits.index+hits[1].length; var length=hits[2].length; var theId=targetElements.length; targetElements[theId]=target; targetText[theId]=hits[2]; //now wrap in link calling mark() found= '<a href="javascript:mark('+theId+','+pos+','+(pos+length)+');" style="border: none; text-decoration:underline; color: #008000;">'+found+'</a>'; } result=result+found; } } else { //recursion var cs=node.childNodes; for (var i=0; i<cs.length; i++) result=result+recurseSearch(cs[i], reg); } return result; } /* Mark a match on users demand */ function mark(theId, start, end){ element=targetElements[theId]; if ("selectionStart" in element){ //MOZ luxury element.selectionStart=start; element.selectionEnd=end; } //scroll window to target location /* if ("scrollIntoView" in element) element.scrollIntoView(); else if ("scrollIntoView" in element.parentNode) element.parentNode.scrollIntoView(); */ var bottom=searcherOverlayWindow.offsetTop+searcherOverlayWindow.offsetHeight+50; if ("createRange" in document){ //MOZ if ("value" in element){ //within interactive elements, we scroll by simulating a user keypress element.setSelectionRange(start,start+1); var ev = document.createEvent ('KeyEvents'); //construct a kepress replacing the current letter with itself. ev.initKeyEvent('keypress', true, true, window,false, false, false, false, 0,element.value.charCodeAt(start)); var readOnly=element.readOnly; element.readOnly=false; //make readable if not so keypress ist accepted. element.dispatchEvent(ev); //causes the scrolling element.readOnly=readOnly; element.setSelectionRange(start, end); element.focus(); }else{ /* a non-interactive element, quite grothesqe way: the only way getting the exact position is to insert a DOM node and let the browser scroll to. inserting the node will actually split the #text-node, so we need to backup and restore the original, then delete the debris. */ var parent=element.parentNode; var dummy=element.cloneNode(true); parent.insertBefore(dummy,element); //connect our dummy to the DOM, enabling positional calculations range=document.createRange(); //dummy range to insert marker node range.setStart(dummy,start); range.setEnd(dummy,end); var span=document.createElement("span");//the marker range.insertNode(span); span.scrollIntoView(); window.scrollBy(0,-bottom); //try to center on screen parent.removeChild(dummy); parent.removeChild(span.nextSibling); //delete renegade #text fragment parent.removeChild(span); //now for the real selection range=document.createRange(); range.setStart(element,start); range.setEnd(element,end); var sel=window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } }else if ("createTextRange" in document.body){//IE: the markup has to be found again range=document.body.createTextRange(); range.moveToElementText(element); //set startpoint range.findText(targetText[theId]); //find again to set endpoint range.select(); range.scrollIntoView(); //easy way, works also in interactive boxes element.focus(); } } /* exterminate the search form overlay */ function kill(){ searcherOverlayWindow.parentNode.removeChild(searcherOverlayWindow); searcherOverlayWindow=null; } /* main installs the search form overlay */ if (searcherOverlayWindow=document.getElementById(id+'TheSearcher')) kill(); searcherOverlayWindow=createDiv(id+'TheSearcher',10,10, '<form name="Form" id="Form" action=""><div style="height: 60px; width:350px; vertical-align: middle; padding:10px;">'+ '<img src="http://en.gibney.org/elements/images/mg-sign.gif" style="float:left; border: none; margin-right:26px;">'+ '<input type="button" value="X" onclick="kill()" style="float: right; text-align:right; vertical-align: top; background-color: silver; border: none; padding:0px; margin: 0px; font-size:12px";>'+ '<span style="font-weight: bold; color: #008000;">Search</span> this site for: <br>'+ '<input type="text" name="Query" id="'+id+'Query" onkeyup="search()"style="width: 150px; margin-top:10px;">'+ '</div></form>'+ '<div id="'+id+'Presenter" style="width: 350px; margin: 10px; max-height: 150px; overflow:auto;"> </div>' ); searcherOverlayWindow.style.zIndex="9999"; document.getElementById(id+"Query").focus(); //EOF
Create a new entry at this position