2 * SearchHighlight plugin for jQuery
4 * Thanks to Scott Yang <http://scott.yang.id.au/>
5 * for the original idea and some code
7 * @author Renato Formato <renatoformato@virgilio.it>
12 * - exact (string, default:"exact")
13 * "exact" : find and highlight the exact words.
14 * "whole" : find partial matches but highlight whole words
15 * "partial": find and highlight partial matches
17 * - style_name (string, default:'hilite')
18 * The class given to the span wrapping the matched words.
20 * - style_name_suffix (boolean, default:true)
21 * If true a different number is added to style_name for every different matched word.
23 * - debug_referrer (string, default:null)
24 * Set a referrer for debugging purpose.
26 * - engines (array of regex, default:null)
27 * Add a new search engine regex to highlight searches coming from new search engines.
28 * The first element is the regex to match the domain.
29 * The second element is the regex to match the query string.
30 * Ex: [/^http:\/\/my\.site\.net/i,/search=([^&]+)/i]
32 * - highlight (string, default:null)
33 * A jQuery selector or object to set the elements enabled for highlight.
34 * If null or no elements are found, all the document is enabled for highlight.
36 * - nohighlight (string, default:null)
37 * A jQuery selector or object to set the elements not enabled for highlight.
38 * This option has priority on highlight.
40 * - keys (string, default:null)
41 * Disable the analisys of the referrer and search for the words given as argument
46 * Included in OpenEMR package on 2/28/2013:
47 * No license/copyright/restrictions/conditions included in script or from site it was
48 * obtained from. Additionally, the author email address no longer exists. For these
49 * reasons, OpenEMR considers this script as public domain.
53 jQuery.fn.SearchHighlight = function(options) {
54 var ref = options.debug_referrer || document.referrer;
55 if(!ref && options.keys==undefined) return this;
57 SearchHighlight.options = $.extend({exact:"exact",style_name:'hilite',style_name_suffix:true},options);
59 if(options.engines) SearchHighlight.engines.unshift(options.engines);
60 var q = options.keys!=undefined?options.keys.split(/[\s,\+\.]+/):SearchHighlight.decodeURL(ref,SearchHighlight.engines);
62 SearchHighlight.buildReplaceTools(q);
63 return this.each(function(){
65 if(el==document) el = $("body")[0];
66 SearchHighlight.hiliteElement(el, q);
71 var SearchHighlight = {
75 [/^http:\/\/(www\.)?google\./i, /q=([^&]+)/i], // Google
76 [/^http:\/\/(www\.)?search\.yahoo\./i, /p=([^&]+)/i], // Yahoo
77 [/^http:\/\/(www\.)?search\.msn\./i, /q=([^&]+)/i], // MSN
78 [/^http:\/\/(www\.)?search\.live\./i, /query=([^&]+)/i], // MSN Live
79 [/^http:\/\/(www\.)?search\.aol\./i, /userQuery=([^&]+)/i], // AOL
80 [/^http:\/\/(www\.)?ask\.com/i, /q=([^&]+)/i], // Ask.com
81 [/^http:\/\/(www\.)?altavista\./i, /q=([^&]+)/i], // AltaVista
82 [/^http:\/\/(www\.)?feedster\./i, /q=([^&]+)/i], // Feedster
83 [/^http:\/\/(www\.)?search\.lycos\./i, /q=([^&]+)/i], // Lycos
84 [/^http:\/\/(www\.)?alltheweb\./i, /q=([^&]+)/i], // AllTheWeb
85 [/^http:\/\/(www\.)?technorati\.com/i, /([^\?\/]+)(?:\?.*)$/i], // Technorati
88 decodeURL: function(URL,reg) {
89 URL = decodeURIComponent(URL);
91 $.each(reg,function(i,n){
93 var match = URL.match(n[1]);
102 query = query.replace(/(\'|")/, '\$1');
103 query = query.split(/[\s,\+\.]+/);
109 [/[\xC0-\xC5\u0100-\u0105]/ig,'a'],
110 [/[\xC7\u0106-\u010D]/ig,'c'],
111 [/[\xC8-\xCB]/ig,'e'],
112 [/[\xCC-\xCF]/ig,'i'],
114 [/[\xD2-\xD6\xD8]/ig,'o'],
115 [/[\u015A-\u0161]/ig,'s'],
116 [/[\u0162-\u0167]/ig,'t'],
117 [/[\xD9-\xDC]/ig,'u'],
119 [/[\x91\x92\u2018\u2019]/ig,'\'']
121 matchAccent : /[\x91\x92\xC0-\xC5\xC7-\xCF\xD1-\xD6\xD8-\xDC\xFF\u0100-\u010D\u015A-\u0167\u2018\u2019]/ig,
122 replaceAccent: function(q) {
123 SearchHighlight.matchAccent.lastIndex = 0;
124 if(SearchHighlight.matchAccent.test(q)) {
125 for(var i=0,l=SearchHighlight.regexAccent.length;i<l;i++)
126 q = q.replace(SearchHighlight.regexAccent[i][0],SearchHighlight.regexAccent[i][1]);
130 escapeRegEx : /((?:\\{2})*)([[\]{}*?|])/g, //the special chars . and + are already gone at this point because they are considered split chars
131 buildReplaceTools : function(query) {
133 $.each(query,function(i,n){
134 if(n = SearchHighlight.replaceAccent(n).replace(SearchHighlight.escapeRegEx,"$1\\$2"))
138 regex = re.join("|");
139 switch(SearchHighlight.options.exact) {
141 regex = '\\b(?:'+regex+')\\b';
144 regex = '\\b\\w*('+regex+')\\w*\\b';
147 switch(SearchHighlight.options.set_case_sensitive) {
149 SearchHighlight.regex = new RegExp(regex, "g");
152 SearchHighlight.regex = new RegExp(regex, "gi");
157 $.each(re,function(i,n){
158 SearchHighlight.subs[n] = SearchHighlight.options.style_name+
159 (SearchHighlight.options.style_name_suffix?i+1:'');
162 nosearch: /s(?:cript|tyle)|textarea/i,
163 hiliteElement: function(el, query) {
164 var opt = SearchHighlight.options, elHighlight, noHighlight;
165 elHighlight = opt.highlight?$(opt.highlight):$("body");
166 if(!elHighlight.length) elHighlight = $("body");
167 noHighlight = opt.nohighlight?$(opt.nohighlight):$([]);
169 elHighlight.each(function(){
170 SearchHighlight.hiliteTree(this,query,noHighlight);
173 hiliteTree : function(el,query,noHighlight) {
174 if(noHighlight.index(el)!=-1) return;
175 var matchIndex = SearchHighlight.options.exact=="whole"?1:0;
176 for(var startIndex=0,endIndex=el.childNodes.length;startIndex<endIndex;startIndex++) {
177 var item = el.childNodes[startIndex];
178 if ( item.nodeType != 8 ) {//comment node
180 if(item.nodeType==3) {
181 var text = item.data, textNoAcc = SearchHighlight.replaceAccent(text);
182 var newtext="",match,index=0;
183 SearchHighlight.regex.lastIndex = 0;
184 while(match = SearchHighlight.regex.exec(textNoAcc)) {
185 /* newtext += text.substr(index,match.index-index)+'<span class="'+
186 SearchHighlight.subs[match[matchIndex]]+'">'+text.substr(match.index,match[0].length)+"</span>";*/
187 newtext += text.substr(index,match.index-index)+'<span class="hilite">'+text.substr(match.index,match[0].length)+"</span>";
188 index = match.index+match[0].length;
191 //add the last part of the text
192 newtext += text.substring(index);
193 var repl = $.merge([],$("<span>"+newtext+"</span>")[0].childNodes);
194 endIndex += repl.length-1;
195 startIndex += repl.length-1;
196 $(item).before(repl).remove();
199 if(item.nodeType==1 && item.nodeName.search(SearchHighlight.nosearch)==-1)
200 SearchHighlight.hiliteTree(item,query,noHighlight);