Mechanism to search patient forms/notes.
authorvinish <vinish@zhservices.com>
Thu, 21 Feb 2013 19:35:46 +0000 (22 01:05 +0530)
committerbradymiller <bradymiller@users.sourceforge.net>
Thu, 28 Feb 2013 08:13:48 +0000 (28 00:13 -0800)
To search forms/notes, go to Patient Summary->Reports->Click the Encounter/notes
you want to search->Then can search them (even can only search specific note types).

Signed-off-by: vinish <vinish@zhservices.com>
(comment also added by bradymiller in library/js/SearchHighlight.js
 to make it explicit that we consider that script to be public domain)

interface/patient_file/report/custom_report.php
interface/themes/style_babyblu.css
interface/themes/style_blue.css
interface/themes/style_metal.css
interface/themes/style_oemr.css
interface/themes/style_purple.css
interface/themes/style_sky_blue.css
library/js/SearchHighlight.js [new file with mode: 0644]

index 64f002e..cf0069f 100644 (file)
@@ -16,6 +16,8 @@ require_once("$srcdir/report.inc");
 require_once("$srcdir/classes/Document.class.php");
 require_once("$srcdir/classes/Note.class.php");
 require_once("$srcdir/formatting.inc.php");
+require_once("$srcdir/htmlspecialchars.inc.php");
+require_once("$srcdir/formdata.inc.php");
 require_once(dirname(__file__) . "/../../../custom/code_types.inc.php");
 
 $PDF_OUTPUT = empty($_POST['pdf']) ? false : true;
@@ -91,14 +93,329 @@ function postToGet($arin) {
       // by div of navigateLink class. Specifically used for CAMOS, but
       // can also be used by other forms that require output in the 
       // encounter listings output, but not in the custom report. ?>
-<style> div.navigateLink {display:none;} </style>
+<style>
+  div.navigateLink {display:none;}
+  .hilite {background-color: #FFFF00;}
+  .hilite2 {background-color: transparent;}
+  mark {background-color: #FFFF00;}
+  .css_button{cursor:pointer;}
+  .next {background-color: #FFFF00;}
+  #search_options{
+    position:fixed;
+    left:0px;
+    top:0px;
+    z-index:10;
+    border-bottom: solid thin #6D6D6D;
+    padding:0% 2% 0% 2.5%;
+  }
+</style>
 
 <?php if (!$PDF_OUTPUT) { ?>
+
+<script type="text/javascript" src="<?php echo $GLOBALS['web_root']?>/library/js/jquery-1.5.js"></script>
+<script type="text/javascript" src="<?php echo $GLOBALS['web_root']?>/library/js/SearchHighlight.js"></script>
+<script type="text/javascript">var $j = jQuery.noConflict();</script>
+
+<script type="text/javascript">
+
+  // Code for search & Highlight
+  function reset_highlight(form_id,form_dir,class_name) { // Removes <span class='hilite' id=''>VAL</span> with VAL
+      $j("."+class_name).each(function(){
+      val = document.getElementById(this.id).innerHTML;
+      $j("#"+this.id).replaceWith(val);
+      
+    });
+  }
+  var res_id = 0;            
+  function doSearch(form_id,form_dir,exact,class_name,keys,case_sensitive) { // Uses jquery SearchHighlight Plug in
+    var options ={};
+    var keys = keys.replace(/^\s+|\s+$/g, '') ;
+    options = {
+      exact     :exact,
+      style_name :class_name,
+      style_name_suffix:false,
+      highlight:'#search_div_'+form_id+'_'+form_dir,
+      keys      :keys,
+      set_case_sensitive:case_sensitive
+      }
+      $j(document).SearchHighlight(options);
+        $j('.'+class_name).each(function(){
+        res_id = res_id+1;
+        $j(this).attr("id",'result_'+res_id);
+      });
+  }
+  
+  function remove_mark(form_id,form_dir){ // Removes all <mark> and </mark> tags
+    var match1 = null;
+    var src_str = document.getElementById('search_div_'+form_id+'_'+form_dir).innerHTML;
+    var re = new RegExp('<mark>',"gi");
+    var match2 = src_str.match(re);
+    if(match2){
+      src_str = src_str.replace(re,'');
+    }
+    var match2 = null;
+    re = new RegExp('</mark>',"gi");
+    if(match2){
+      src_str = src_str.replace(re,'');
+    }
+    document.getElementById('search_div_'+form_id+'_'+form_dir).innerHTML=src_str;
+  }
+  function mark_hilight(form_id,form_dir,keys,case_sensitive){ // Adds <mark>match_val</mark> tags    
+    keys = keys.replace(/^\s+|\s+$/g, '') ;
+    if(keys == '') return;
+    var src_str = $j('#search_div_'+form_id+'_'+form_dir).html();
+    var term = keys;
+    if((/\s+/).test(term) == true || (/['""-]{1,}/).test(term) == true){
+      term = term.replace(/(\s+)/g,"(<[^>]+>)*$1(<[^>]+>)*");
+      if(case_sensitive == true){
+        var pattern = new RegExp("("+term+")", "g");
+      }
+      else{
+        var pattern = new RegExp("("+term+")", "ig");
+      }
+      src_str = src_str.replace(/[\s\r\n]{1,}/g, ' '); // Replace text area newline or multiple spaces with single space
+      src_str = src_str.replace(pattern, "<mark class='hilite'>$1</mark>");
+      src_str = src_str.replace(/(<mark class=\'hilite\'>[^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/g,"$1</mark>$2<mark class='hilite'>$4");
+      $j('#search_div_'+form_id+'_'+form_dir).html(src_str);
+        $j('.hilite').each(function(){
+        res_id = res_id+1;
+        $j(this).attr("id",'result_'+res_id);
+      });
+    }else{
+      if(case_sensitive == true)
+      doSearch(form_id,form_dir,'partial','hilite',keys,'true');
+      else
+      doSearch(form_id,form_dir,'partial','hilite',keys,'false');
+    }
+  }
+  
+  var forms_array;
+  var res_array   = Array();
+  function find_all(){ // for each report the function mark_hilight() is called
+    case_sensitive = false;
+    if ($j('#search_case').attr('checked')) {
+        case_sensitive = true;
+    }
+    var keys = document.getElementById('search_element').value;
+    var match = null;
+    match = keys.match(/[\^\$\.\|\?\+\(\)\\~`\!@#%&\+={}<>]{1,}/);
+    if(match){
+      document.getElementById('alert_msg').innerHTML='<?php echo xla('Special characters are not allowed');?>..!';
+      return;
+    }
+    else{
+      document.getElementById('alert_msg').innerHTML='';
+    }
+    
+    forms_arr = document.getElementById('forms_to_search');
+    for (var i = 0; i < forms_arr.options.length; i++) {
+     if(forms_arr.options[i].selected ==true){
+          $j('.class_'+forms_arr.options[i].value).each(function(){
+          id_arr = this.id.split('search_div_');  
+          var re = new RegExp('_','i');
+          new_id = id_arr[1].replace(re, "|");
+          new_id_arr = new_id.split('|');
+          form_id = new_id_arr[0];
+          form_dir = new_id_arr[1];
+          mark_hilight(form_id,form_dir,keys,case_sensitive);
+        });
+          
+      }
+    }
+    if($j('.hilite').length <1){
+      if(keys != '')
+      document.getElementById('alert_msg').innerHTML='<?php echo xla('No results found');?>..!';
+    }
+    else{
+      document.getElementById('alert_msg').innerHTML='';
+      f_id = $j('.hilite:first').attr('id');
+      element = document.getElementById(f_id);
+      element.scrollIntoView(false);
+    }
+
+  }
+  
+  function remove_mark_all(){ // clears previous search results if exists
+    $j('.report_search_div').each(function(){
+      var id_arr = this.id.split('search_div_');  
+      var re = new RegExp('_','i');
+      var new_id = id_arr[1].replace(re, "|");
+      var new_id_arr = new_id.split('|');
+      var form_id = new_id_arr[0];
+      var form_dir = new_id_arr[1];
+      reset_highlight(form_id,form_dir,'hilite');
+      reset_highlight(form_id,form_dir,'hilite2');
+      remove_mark(form_id,form_dir);
+      res_id = 0;
+      res_array =[];
+    });
+  }
+  //
+  var last_visited = -1;
+  var last_clicked = "";
+  var cur_res =0;
+  function next(w_count){
+    cur_res++;
+    remove_mark_all();
+    find_all();
+    var index = -1;
+    if(!($j(".hilite")[0])) {
+      return;
+    }
+    $j('.hilite').each(function(){
+      if($j(this).is(":visible")){
+        index = index+1;
+        res_array[index] = this.id;
+      }
+    });
+    $j('.hilite').addClass("hilite2");
+    $j('.hilite').removeClass("hilite");
+    var array_count = res_array.length;
+    if(last_clicked == "prev"){
+      last_visited = last_visited + (w_count-1);
+     }
+     last_clicked = "next";
+    for(k=0;k<w_count;k++){
+      last_visited ++;
+        if(last_visited == array_count){
+          cur_res = 0;
+          last_visited = -1;
+          next(w_count);
+          return;
+        }
+        $j("#"+res_array[last_visited]).addClass("next");
+    }
+    element = document.getElementById(res_array[last_visited]);
+    element.scrollIntoView(false);
+    
+  }
+
+  function prev(w_count){
+    cur_res--;
+    remove_mark_all();
+    find_all();
+    var index = -1;
+    if(!($j(".hilite")[0])) {
+      return;
+    }
+    $j('.hilite').each(function(){
+      if($j(this).is(":visible")){
+        index = index+1;
+        res_array[index] = this.id;
+      }
+    });
+     $j('.hilite').addClass("hilite2");
+     $j('.hilite').removeClass("hilite");
+     var array_count = res_array.length;
+     if(last_clicked == "next"){
+      last_visited = last_visited - (w_count-1);
+     }
+     last_clicked = "prev";
+    for(k=0;k<w_count;k++){
+      last_visited --;
+      if(last_visited < 0){
+        cur_res = (array_count/w_count) + 1;
+        last_visited = array_count;
+        prev(w_count);
+        return;
+      }
+    $j("#"+res_array[last_visited]).addClass("next");
+    
+    }
+    
+    element = document.getElementById(res_array[last_visited]);
+    element.scrollIntoView(false);
+  }
+  function clear_last_visit(){
+    last_visited = -1;
+    cur_res = 0;
+    res_array = [];
+    last_clicked = "";
+  }
+
+  function get_word_count(form_id,form_dir,keys,case_sensitive){
+    keys = keys.replace(/^\s+|\s+$/g, '') ;
+    if(keys == '') return;
+    var src_str = $j('#search_div_'+form_id+'_'+form_dir).html();
+    var term = keys;
+    if((/\s+/).test(term) == true){
+      term = term.replace(/(\s+)/g,"(<[^>]+>)*$1(<[^>]+>)*");
+      if(case_sensitive == true){
+        var pattern = new RegExp("("+term+")", "");
+      }
+      else{
+        var pattern = new RegExp("("+term+")", "i");
+      }
+      src_str = src_str.replace(/[\s\r\n]{1,}/g, ' '); // Replace text area newline or multiple spaces with single space
+      src_str = src_str.replace(pattern, "<mark class='hilite'>$1</mark>");
+      src_str = src_str.replace(/(<mark class=\'hilite\'>[^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/,"$1</mark>$2<mark class='hilite'>$4");
+      var res =[];
+      res = src_str.match(/<mark class=\'hilite\'>/g);
+      if(res != null){
+        return res.length;
+      }
+    }else{
+      return 1;
+    } 
+  }
+  
+  function next_prev(action){
+    var w_count =0;
+    case_sensitive = false;
+    if ($j('#search_case').attr('checked')) {
+        case_sensitive = true;
+    }
+    var keys = document.getElementById('search_element').value;
+    var match = null;
+    match = keys.match(/[\^\$\.\|\?\+\(\)\\~`\!@#%&\+={}<>]{1,}/);
+    if(match){
+      document.getElementById('alert_msg').innerHTML='<?php echo xla('Special characters are not allowed');?>..!';
+      return;
+    }
+    else{
+      document.getElementById('alert_msg').innerHTML='';
+    }
+    forms_arr = document.getElementById('forms_to_search');
+    for (var i = 0; i < forms_arr.options.length; i++) {
+     if(forms_arr.options[i].selected ==true){
+          $j('.class_'+forms_arr.options[i].value).each(function(){
+          id_arr = this.id.split('search_div_');  
+          var re = new RegExp('_','i');
+          new_id = id_arr[1].replace(re, "|");
+          new_id_arr = new_id.split('|');
+          form_id = new_id_arr[0];
+          form_dir = new_id_arr[1];
+          w_count = get_word_count(form_id,form_dir,keys,case_sensitive);
+        });
+        if(!isNaN(w_count)){
+          break;
+        }
+      }
+    }
+    if(w_count <1){
+      if(keys != '')
+      document.getElementById('alert_msg').innerHTML='<?php echo xla('No results found');?>..!';
+    }
+    else{
+      document.getElementById('alert_msg').innerHTML='';
+      if(action == 'next'){
+       next(w_count);
+      }
+      else if (action == 'prev'){
+       prev(w_count);
+      }
+      var tot_res = res_array.length/w_count;
+         if(tot_res > 0){
+               document.getElementById('alert_msg').innerHTML='<?php echo xla('Showing result');?> '+cur_res+' <?php echo xla('of');?> '+tot_res;
+         }
+    }
+    
+  }
+</script>
 </head>
-<body class="body_top">
+<body class="body_top" style="padding-top:95px;">
 <?php } ?>
-
-<div id="report_custom">  <!-- large outer DIV -->
+<div id="report_custom" style="width:100%;">  <!-- large outer DIV -->
 
 <?php
 if (sizeof($_GET) > 0) { $ar = $_GET; }
@@ -140,7 +457,6 @@ if ($printable) {
 
 } 
 else { // not printable
-
 ?>
 
 <a href="patient_report.php">
@@ -150,8 +466,63 @@ else { // not printable
 <a href="custom_report.php?printable=1&<?php print postToGet($ar); ?>" class='link_submit' target='new'>
  [<?php xl('Printable Version','e'); ?>]
 </a><br>
-
-<?php } // end not printable ?>
+<div class="report_search_bar" style="width:100%;" id="search_options">
+  <table style="width:100%;">
+    <tr>
+      <td>
+        <input type="text" onkeyup="clear_last_visit();remove_mark_all();find_all();" name="search_element" id="search_element" style="width:180px;"/>
+      </td>
+      <td>
+         <a class="css_button" onclick="clear_last_visit();remove_mark_all();find_all();" ><span><?php echo xlt('Find'); ?></span></a>
+      </td>
+      <td>
+         <a class="css_button" onclick="next_prev('prev');" ><span><?php echo xlt('Prev'); ?></span></a>
+      </td>
+      <td>
+         <a class="css_button" onclick="next_prev('next');" ><span><?php echo xlt('Next'); ?></span></a>
+      </td>
+      <td>
+        <input type="checkbox" onclick="clear_last_visit();remove_mark_all();find_all();" name="search_case" id="search_case" />
+      </td>
+      <td>
+        <span><?php echo xlt('Match case'); ?></span>
+      </td>
+      <td style="padding-left:10px;">
+        <span class="text"><b><?php echo xlt('Search In'); ?>:</b></span>
+        <br>
+        <?php
+        $form_id_arr = array();
+        $form_dir_arr = array();
+        $last_key ='';
+        //ksort($ar);
+        foreach ($ar as $key_search => $val_search) {
+            if ($key_search == 'pdf' || $key_search == '' ) continue;
+            if (($auth_notes_a || $auth_notes || $auth_coding_a || $auth_coding || $auth_med || $auth_relaxed)) {
+                        preg_match('/^(.*)_(\d+)$/', $key_search, $res_search);
+                        $form_id_arr[] = add_escape_custom($res_search[2]);
+                         $form_dir_arr[] = add_escape_custom($res_search[1]);
+            }
+        }
+        //echo json_encode(json_encode($array_key_id));
+        if(sizeof($form_id_arr)>0){
+          $query = "SELECT DISTINCT(form_name),formdir FROM forms WHERE form_id IN ( '".implode("','",$form_id_arr)."') AND formdir IN ( '".implode("','",$form_dir_arr)."')";
+          $arr = sqlStatement($query);
+          echo "<select multiple size='4' style='width:300px;' id='forms_to_search' onchange='clear_last_visit();remove_mark_all();find_all();' >";
+          while($res_forms_ids = sqlFetchArray($arr)){
+            echo "<option value='".attr($res_forms_ids['formdir'])."' selected>".text($res_forms_ids['form_name'])."</option>";
+          }
+          echo "</select>";
+        }
+        ?>
+      </td>
+      <td style="padding-left:10px;;width:30%;">
+        <span id ='alert_msg' style='color:red;'></span>
+      </td>
+    </tr>
+  </table>
+</div>
+<?php
+} // end not printable ?>
 
 <?php
 
@@ -545,10 +916,16 @@ foreach ($ar as $key => $val) {
                 echo "<br>\n";
    
                 // call the report function for the form
+                ?>                
+                <div name="search_div" id="search_div_<?php echo attr($form_id)?>_<?php echo attr($res[1])?>" class="report_search_div class_<?php echo attr($res[1]); ?>">
+                <?php
                 if (substr($res[1],0,3) == 'LBF')
                   call_user_func("lbf_report", $pid, $form_encounter, $N, $form_id, $res[1]);
                 else
                   call_user_func($res[1] . "_report", $pid, $form_encounter, $N, $form_id);
+                ?>
+                </div>
+                <?php
 
                 if ($res[1] == 'newpatient') {
                     // display billing info
index 5dfa066..f6bf825 100644 (file)
@@ -1056,4 +1056,7 @@ font-size:9pt;
 }
 .cke_contents {
 height: 300px !important;
+}
+.report_search_bar{
+       background-color:#C9DCFF;
 }
\ No newline at end of file
index 43d3e03..bd095b8 100644 (file)
@@ -313,4 +313,8 @@ height: 300px !important;
 .formtable tr td {
         line-height: 24px;
     }
+.report_search_bar{
+       background-color:#E1E4E9;
+}
+
 
index 2b11dea..cdcc5dd 100644 (file)
@@ -1064,4 +1064,6 @@ height: 300px !important;
 .formtable tr td {
         line-height: 24px;
     }
-
+.report_search_bar{
+       background-color:#E1E4E9;
+}
index b34f102..da3e037 100644 (file)
@@ -1065,4 +1065,6 @@ height: 300px !important;
 .formtable tr td {
         line-height: 24px;
     }
-
+.report_search_bar{
+       background-color:#EADDB4;
+}
index 27e4dc1..f38e641 100644 (file)
@@ -1065,4 +1065,7 @@ height: 300px !important;
 .formtable tr td {
         line-height: 24px;
     }
+.report_search_bar{
+       background-color:#C9DCFF;
+}
 
index 2756d2a..21de84f 100644 (file)
@@ -1065,4 +1065,7 @@ height: 300px !important;
 .formtable tr td {
         line-height: 24px;
     }
+.report_search_bar{
+       background-color:#A1B0CF;
+}
 
diff --git a/library/js/SearchHighlight.js b/library/js/SearchHighlight.js
new file mode 100644 (file)
index 0000000..402a4ec
--- /dev/null
@@ -0,0 +1,206 @@
+/**
+ * SearchHighlight plugin for jQuery
+ * 
+ * Thanks to Scott Yang <http://scott.yang.id.au/>
+ * for the original idea and some code
+ *    
+ * @author Renato Formato <renatoformato@virgilio.it> 
+ *  
+ * @version 0.33
+ *
+ *  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
+ *     
+ *  - style_name (string, default:'hilite')
+ *    The class given to the span 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    
+ *    
+ */
+
+/**
+ * Included in OpenEMR package on 2/28/2013:
+ * No license/copyright/restrictions/conditions included in script or from site it was
+ * obtained from. Additionally, the author email address no longer exists. For these
+ * reasons, OpenEMR considers this script as public domain.
+ */
+
+(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",style_name:'hilite',style_name_suffix:true},options);
+    
+    if(options.engines) SearchHighlight.engines.unshift(options.engines);  
+    var q = options.keys!=undefined?options.keys.split(/[\s,\+\.]+/):SearchHighlight.decodeURL(ref,SearchHighlight.engines);
+    if(q && q.join("")) {
+      SearchHighlight.buildReplaceTools(q);
+      return this.each(function(){
+        var el = this;
+        if(el==document) el = $("body")[0];
+        SearchHighlight.hiliteElement(el, q); 
+      })
+    } else return this;
+  }    
+
+  var SearchHighlight = {
+    options: {},
+    regex: [],
+    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) {
+      URL = decodeURIComponent(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];
+            return false;
+          }
+        }
+      })
+      
+      if (query) {
+      query = query.replace(/(\'|")/, '\$1');
+      query = query.split(/[\s,\+\.]+/);
+      }
+      
+      return query;
+    },
+               regexAccent : [
+      [/[\xC0-\xC5\u0100-\u0105]/ig,'a'],
+      [/[\xC7\u0106-\u010D]/ig,'c'],
+      [/[\xC8-\xCB]/ig,'e'],
+      [/[\xCC-\xCF]/ig,'i'],
+      [/\xD1/ig,'n'],
+      [/[\xD2-\xD6\xD8]/ig,'o'],
+      [/[\u015A-\u0161]/ig,'s'],
+      [/[\u0162-\u0167]/ig,'t'],
+      [/[\xD9-\xDC]/ig,'u'],
+      [/\xFF/ig,'y'],
+      [/[\x91\x92\u2018\u2019]/ig,'\'']
+    ],
+    matchAccent : /[\x91\x92\xC0-\xC5\xC7-\xCF\xD1-\xD6\xD8-\xDC\xFF\u0100-\u010D\u015A-\u0167\u2018\u2019]/ig,  
+               replaceAccent: function(q) {
+                 SearchHighlight.matchAccent.lastIndex = 0;
+      if(SearchHighlight.matchAccent.test(q)) {
+        for(var i=0,l=SearchHighlight.regexAccent.length;i<l;i++)
+          q = q.replace(SearchHighlight.regexAccent[i][0],SearchHighlight.regexAccent[i][1]);
+      }
+      return q;
+    },
+    escapeRegEx : /((?:\\{2})*)([[\]{}*?|])/g, //the special chars . and + are already gone at this point because they are considered split chars
+    buildReplaceTools : function(query) {
+        var re = [], regex;
+        $.each(query,function(i,n){
+            if(n = SearchHighlight.replaceAccent(n).replace(SearchHighlight.escapeRegEx,"$1\\$2"))
+              re.push(n);        
+        });
+        
+        regex = re.join("|");
+        switch(SearchHighlight.options.exact) {
+          case "exact":
+            regex = '\\b(?:'+regex+')\\b';
+            break;
+          case "whole":
+            regex = '\\b\\w*('+regex+')\\w*\\b';
+            break;
+        }
+               switch(SearchHighlight.options.set_case_sensitive) {
+                                 case "true":
+                                       SearchHighlight.regex = new RegExp(regex, "g");
+                                       break;
+                                 case "false":
+                                       SearchHighlight.regex = new RegExp(regex, "gi");
+                                       break;
+               }               
+        
+        
+        $.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, query) {
+        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,query,noHighlight);
+        });
+    },
+    hiliteTree : function(el,query,noHighlight) {
+        if(noHighlight.index(el)!=-1) return;
+        var matchIndex = SearchHighlight.options.exact=="whole"?1:0;
+        for(var startIndex=0,endIndex=el.childNodes.length;startIndex<endIndex;startIndex++) {
+          var item = el.childNodes[startIndex];
+          if ( item.nodeType != 8 ) {//comment node
+                                 //text node
+            if(item.nodeType==3) {
+              var text = item.data, textNoAcc = SearchHighlight.replaceAccent(text);
+              var newtext="",match,index=0;
+              SearchHighlight.regex.lastIndex = 0;
+              while(match = SearchHighlight.regex.exec(textNoAcc)) {
+               /* newtext += text.substr(index,match.index-index)+'<span class="'+
+                SearchHighlight.subs[match[matchIndex]]+'">'+text.substr(match.index,match[0].length)+"</span>";*/
+                               newtext += text.substr(index,match.index-index)+'<span class="hilite">'+text.substr(match.index,match[0].length)+"</span>";
+                index = match.index+match[0].length;
+              }
+              if(newtext) {
+                //add the last part of the text
+                newtext += text.substring(index);
+                var repl = $.merge([],$("<span>"+newtext+"</span>")[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,query,noHighlight);
+            }  
+          }
+        }    
+    }
+  };
+})(jQuery)