/**
* SearchHighlight plugin for jQuery
*
* Thanks to Scott Yang
* for the original idea and some code
*
* @author Renato Formato
*
* @version 0.37 (9/1/2009)
*
* Options
* - exact (string, default:"exact")
* "exact" : find and highlight the exact words.
* "whole" : find partial matches but highlight whole words
* "partial": find and highlight partial matches
*
* - tag_name (string, default:'span')
* The tag that is used to wrap the matched words
*
* - style_name (string, default:'hilite')
* The class given to the tag wrapping the matched words.
*
* - style_name_suffix (boolean, default:true)
* If true a different number is added to style_name for every different matched word.
*
* - debug_referrer (string, default:null)
* Set a referrer for debugging purpose.
*
* - engines (array of regex, default:null)
* Add a new search engine regex to highlight searches coming from new search engines.
* The first element is the regex to match the domain.
* The second element is the regex to match the query string.
* Ex: [/^http:\/\/my\.site\.net/i,/search=([^&]+)/i]
*
* - highlight (string, default:null)
* A jQuery selector or object to set the elements enabled for highlight.
* If null or no elements are found, all the document is enabled for highlight.
*
* - nohighlight (string, default:null)
* A jQuery selector or object to set the elements not enabled for highlight.
* This option has priority on highlight.
*
* - keys (string, default:null)
* Disable the analisys of the referrer and search for the words given as argument
*
* - min_length (number, default:null)
* Set the minimun length of a key
*
*/
if (window.jQuery)
(function($){
jQuery.fn.SearchHighlight = function(options) {
var ref = options.debug_referrer || document.referrer;
if(!ref && options.keys==undefined) return this;
SearchHighlight.options = $.extend({exact:"exact",tag_name:'span',style_name:'hilite',style_name_suffix:true},options);
if(options.engines) SearchHighlight.engines.unshift(options.engines);
var q = SearchHighlight.splitKeywords(options.keys!=undefined?options.keys.toLowerCase():SearchHighlight.decodeURL(ref,SearchHighlight.engines));
if(q && q.join("")) {
SearchHighlight.buildReplaceTools(q);
if(!SearchHighlight.regex) return this;
return this.each(function(){
var el = this;
if(el==document) el = $("body")[0];
SearchHighlight.hiliteElement(el);
})
} else return this;
};
var SearchHighlight = {
options: {},
regex: null,
engines: [
[/^http:\/\/(www\.)?google\./i, /q=([^&]+)/i], // Google
[/^http:\/\/(www\.)?search\.yahoo\./i, /p=([^&]+)/i], // Yahoo
[/^http:\/\/(www\.)?search\.msn\./i, /q=([^&]+)/i], // MSN
[/^http:\/\/(www\.)?search\.live\./i, /query=([^&]+)/i], // MSN Live
[/^http:\/\/(www\.)?search\.aol\./i, /userQuery=([^&]+)/i], // AOL
[/^http:\/\/(www\.)?ask\.com/i, /q=([^&]+)/i], // Ask.com
[/^http:\/\/(www\.)?altavista\./i, /q=([^&]+)/i], // AltaVista
[/^http:\/\/(www\.)?feedster\./i, /q=([^&]+)/i], // Feedster
[/^http:\/\/(www\.)?search\.lycos\./i, /q=([^&]+)/i], // Lycos
[/^http:\/\/(www\.)?alltheweb\./i, /q=([^&]+)/i], // AllTheWeb
[/^http:\/\/(www\.)?technorati\.com/i, /([^\?\/]+)(?:\?.*)$/i] // Technorati
],
subs: {},
decodeURL: function(URL,reg) {
//try to properly escape not UTF-8 URI encoded chars
try {
URL = decodeURIComponent(URL);
} catch (e) {
URL = unescape(URL);
}
var query = null;
$.each(reg,function(i,n){
if(n[0].test(URL)) {
var match = URL.match(n[1]);
if(match) {
query = match[1].toLowerCase();
return false;
}
}
});
return query;
},
splitKeywords: function(query) {
if(query) {
//do not split keywords enclosed by "
var m = query.match(/"([^"]*)"/g);
if(m)
for(var i=0, ml=m.length;i=SearchHighlight.options.min_length)
if(n = SearchHighlight.replaceAccent(n).replace(SearchHighlight.escapeRegEx,"$1\\$2"))
re.push(n);
});
if(!re.length) return;
regex = re.join("|");
switch(SearchHighlight.options.exact) {
case "exact":
regex = '\\b(?:'+regex+')\\b';
break;
case "whole":
regex = '\\b\\w*('+regex+')\\w*\\b';
break;
}
SearchHighlight.regex = new RegExp(regex, "gi");
$.each(re,function(i,n){
SearchHighlight.subs[n] = SearchHighlight.options.style_name+
(SearchHighlight.options.style_name_suffix?i+1:'');
});
},
nosearch: /s(?:cript|tyle)|textarea/i,
hiliteElement: function(el) {
var opt = SearchHighlight.options, elHighlight, noHighlight;
elHighlight = opt.highlight?$(opt.highlight):$("body");
if(!elHighlight.length) elHighlight = $("body");
noHighlight = opt.nohighlight?$(opt.nohighlight):$([]);
elHighlight.each(function(){
SearchHighlight.hiliteTree(this,noHighlight);
});
},
hiliteTree : function(el,noHighlight) {
if(noHighlight.index(el)!=-1) return;
var matchIndex = SearchHighlight.options.exact=="whole"?1:0;
for(var startIndex=0,endIndex=el.childNodes.length;startIndex'+SearchHighlight.fixTags(text.substr(match.index,match[0].length))+""+SearchHighlight.options.tag_name+">";
index = match.index+match[0].length;
}
if(newtext) {
//add the last part of the text
newtext += SearchHighlight.fixTags(text.substring(index));
var repl = $.merge([],$("<"+SearchHighlight.options.tag_name+">"+newtext+""+SearchHighlight.options.tag_name+">")[0].childNodes);
endIndex += repl.length-1;
startIndex += repl.length-1;
$(item).before(repl).remove();
}
} else {
if(item.nodeType==1 && item.nodeName.search(SearchHighlight.nosearch)==-1)
SearchHighlight.hiliteTree(item,noHighlight);
}
}
}
},
fixTags : function(text) {
return text.replace("<","<").replace(">",">");
}
};
})(jQuery)