Improvements in the translation table building script, take 2.
[openemr.git] / library / js / SearchHighlight.js
blob402a4ec126e43506b32db03ad902d2d10f709f22
1 /**
2  * SearchHighlight plugin for jQuery
3  * 
4  * Thanks to Scott Yang <http://scott.yang.id.au/>
5  * for the original idea and some code
6  *    
7  * @author Renato Formato <renatoformato@virgilio.it> 
8  *  
9  * @version 0.33
10  *
11  *  Options
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
16  *     
17  *  - style_name (string, default:'hilite')
18  *    The class given to the span wrapping the matched words.
19  *     
20  *  - style_name_suffix (boolean, default:true)
21  *    If true a different number is added to style_name for every different matched word.
22  *     
23  *  - debug_referrer (string, default:null)
24  *    Set a referrer for debugging purpose.
25  *     
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]        
31  *            
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.
35  *        
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. 
39  *    
40  *  - keys (string, default:null)
41  *    Disable the analisys of the referrer and search for the words given as argument    
42  *    
43  */
45 /**
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.
50  */
52 (function($){
53   jQuery.fn.SearchHighlight = function(options) {
54     var ref = options.debug_referrer || document.referrer;
55     if(!ref && options.keys==undefined) return this;
56     
57     SearchHighlight.options = $.extend({exact:"exact",style_name:'hilite',style_name_suffix:true},options);
58     
59     if(options.engines) SearchHighlight.engines.unshift(options.engines);  
60     var q = options.keys!=undefined?options.keys.split(/[\s,\+\.]+/):SearchHighlight.decodeURL(ref,SearchHighlight.engines);
61     if(q && q.join("")) {
62       SearchHighlight.buildReplaceTools(q);
63       return this.each(function(){
64         var el = this;
65         if(el==document) el = $("body")[0];
66         SearchHighlight.hiliteElement(el, q); 
67       })
68     } else return this;
69   }    
71   var SearchHighlight = {
72     options: {},
73     regex: [],
74     engines: [
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
86     ],
87     subs: {},
88     decodeURL: function(URL,reg) {
89       URL = decodeURIComponent(URL);
90       var query = null;
91       $.each(reg,function(i,n){
92         if(n[0].test(URL)) {
93           var match = URL.match(n[1]);
94           if(match) {
95             query = match[1];
96             return false;
97           }
98         }
99       })
100       
101       if (query) {
102       query = query.replace(/(\'|")/, '\$1');
103       query = query.split(/[\s,\+\.]+/);
104       }
105       
106       return query;
107     },
108                 regexAccent : [
109       [/[\xC0-\xC5\u0100-\u0105]/ig,'a'],
110       [/[\xC7\u0106-\u010D]/ig,'c'],
111       [/[\xC8-\xCB]/ig,'e'],
112       [/[\xCC-\xCF]/ig,'i'],
113       [/\xD1/ig,'n'],
114       [/[\xD2-\xD6\xD8]/ig,'o'],
115       [/[\u015A-\u0161]/ig,'s'],
116       [/[\u0162-\u0167]/ig,'t'],
117       [/[\xD9-\xDC]/ig,'u'],
118       [/\xFF/ig,'y'],
119       [/[\x91\x92\u2018\u2019]/ig,'\'']
120     ],
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]);
127       }
128       return q;
129     },
130     escapeRegEx : /((?:\\{2})*)([[\]{}*?|])/g, //the special chars . and + are already gone at this point because they are considered split chars
131     buildReplaceTools : function(query) {
132         var re = [], regex;
133         $.each(query,function(i,n){
134             if(n = SearchHighlight.replaceAccent(n).replace(SearchHighlight.escapeRegEx,"$1\\$2"))
135               re.push(n);        
136         });
137         
138         regex = re.join("|");
139         switch(SearchHighlight.options.exact) {
140           case "exact":
141             regex = '\\b(?:'+regex+')\\b';
142             break;
143           case "whole":
144             regex = '\\b\\w*('+regex+')\\w*\\b';
145             break;
146         }
147                 switch(SearchHighlight.options.set_case_sensitive) {
148                                   case "true":
149                                         SearchHighlight.regex = new RegExp(regex, "g");
150                                         break;
151                                   case "false":
152                                         SearchHighlight.regex = new RegExp(regex, "gi");
153                                         break;
154                 }               
155         
156         
157         $.each(re,function(i,n){
158             SearchHighlight.subs[n] = SearchHighlight.options.style_name+
159               (SearchHighlight.options.style_name_suffix?i+1:''); 
160         });       
161     },
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):$([]);
168                 
169         elHighlight.each(function(){
170           SearchHighlight.hiliteTree(this,query,noHighlight);
171         });
172     },
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
179                                   //text 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;
189               }
190               if(newtext) {
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();
197               }                
198             } else {
199               if(item.nodeType==1 && item.nodeName.search(SearchHighlight.nosearch)==-1)
200                   SearchHighlight.hiliteTree(item,query,noHighlight);
201             }   
202           }
203         }    
204     }
205   };
206 })(jQuery)