2  * Search Engine Keyword Highlight (http://fucoder.com/code/se-hilite/)
 
   4  * This module can be imported by any HTML page, and it would analyse the
 
   5  * referrer for search engine keywords, and then highlight those keywords on
 
   6  * the page, by wrapping them around <span class="hilite">...</span> tags.
 
   7  * Document can then define styles else where to provide visual feedbacks.
 
  11  *   In HTML. Add the following line towards the end of the document.
 
  13  *     <script type="text/javascript" src="se_hilite.js"></script>
 
  15  *   In CSS, define the following style:
 
  17  *     .hilite { background-color: #ff0; }
 
  19  *   If Hilite.style_name_suffix is true, then define the follow styles:
 
  21  *     .hilite1 { background-color: #ff0; }
 
  22  *     .hilite2 { background-color: #f0f; }
 
  23  *     .hilite3 { background-color: #0ff; }
 
  26  * @author Scott Yang <http://scott.yang.id.au/>
 
  33      * Element ID to be highlighted. If set, then only content inside this DOM
 
  34      * element will be highlighted, otherwise everything inside document.body
 
  40      * Whether we are matching an exact word. For example, searching for
 
  41      * "highlight" will only match "highlight" but not "highlighting" if exact
 
  47      * Maximum number of DOM nodes to test, before handing the control back to
 
  48      * the GUI thread. This prevents locking up the UI when parsing and
 
  49      * replacing inside a large document.
 
  54      * Whether to automatically hilite a section of the HTML document, by
 
  55      * binding the "Hilite.hilite()" to window.onload() event. If this
 
  56      * attribute is set to false, you can still manually trigger the hilite by
 
  57      * calling Hilite.hilite() in Javascript after document has been fully
 
  63      * Name of the style to be used. Default to 'hilite'.
 
  68      * Whether to use different style names for different search keywords by
 
  69      * appending a number starting from 1, i.e. hilite1, hilite2, etc.
 
  71     style_name_suffix: true,
 
  74      * Set it to override the document.referrer string. Used for debugging
 
  80 Hilite.search_engines = [
 
  81     ['google\\.', 'q'],                             // Google
 
  82     ['search\\.yahoo\\.', 'p'],                     // Yahoo
 
  83     ['search\\.msn\\.', 'q'],                       // MSN
 
  84     ['search\\.live\\.', 'query'],                  // MSN Live
 
  85     ['search\\.aol\\.', 'userQuery'],               // AOL
 
  86     ['ask\\.com', 'q'],                             // Ask.com
 
  87     ['altavista\\.', 'q'],                          // AltaVista
 
  88     ['feedster\\.', 'q'],                           // Feedster
 
  89     ['search\\.lycos\\.', 'q'],                     // Lycos
 
  90     ['alltheweb\\.', 'q'],                          // AllTheWeb
 
  91     ['technorati\\.com/search/([^\\?/]+)', 1],      // Technorati
 
  92     ['dogpile\\.com/info\\.dogpl/search/web/([^\\?/]+)', 1, true] // DogPile
 
  96  * Decode the referrer string and return a list of search keywords.
 
  98 Hilite.decodeReferrer = function(referrer) {
 
 100     var regex = new RegExp('');
 
 102     for (var i = 0; i < Hilite.search_engines.length; i ++) {
 
 103         var se = Hilite.search_engines[i];
 
 104         regex.compile('^http://(www\\.)?' + se[0], 'i');
 
 105         var match = referrer.match(regex);
 
 109                 result = Hilite.decodeReferrerQS(referrer, se[1]);
 
 111                 result = match[se[1] + 1];
 
 114                 result = decodeURIComponent(result);
 
 115                 // XXX: DogPile's URI requires decoding twice.
 
 116                 if (se.length > 2 && se[2])
 
 117                     result = decodeURIComponent(result);
 
 118                 result = result.replace(/\'|"/g, '');
 
 119                 result = result.split(/[\s,\+\.]+/);
 
 128 Hilite.decodeReferrerQS = function(referrer, match) {
 
 129     var idx = referrer.indexOf('?');
 
 132         var qs = new String(referrer.substring(idx + 1));
 
 135         while ((idx >= 0) && ((idx2 = qs.indexOf('=', idx)) >= 0)) {
 
 137             key = qs.substring(idx, idx2);
 
 138             idx = qs.indexOf('&', idx2) + 1;
 
 141                     return qs.substring(idx2+1);
 
 143                     return qs.substring(idx2+1, idx - 1);
 
 155  * Highlight a DOM element with a list of keywords.
 
 157 Hilite.hiliteElement = function(elm, query) {
 
 158     if (!query || elm.childNodes.length == 0)
 
 161     var qre = new Array();
 
 162     for (var i = 0; i < query.length; i ++) {
 
 163         query[i] = query[i].toLowerCase();
 
 165             qre.push('\\b'+query[i]+'\\b');
 
 170     qre = new RegExp(qre.join("|"), "i");
 
 172     var stylemapper = {};
 
 173     for (var i = 0; i < query.length; i ++) {
 
 174         if (Hilite.style_name_suffix)
 
 175             stylemapper[query[i]] = Hilite.style_name+(i+1);
 
 177             stylemapper[query[i]] = Hilite.style_name;
 
 180     var textproc = function(node) {
 
 181         var match = qre.exec(node.data);
 
 185             var node2 = node.splitText(match.index);
 
 186             var node3 = node2.splitText(val.length);
 
 187             var span = node.ownerDocument.createElement('SPAN');
 
 188             node.parentNode.replaceChild(span, node2);
 
 189             span.className = stylemapper[val.toLowerCase()];
 
 190             span.appendChild(node2);
 
 196     Hilite.walkElements(elm.childNodes[0], 1, textproc);
 
 200  * Highlight a HTML document using keywords extracted from document.referrer.
 
 201  * This is the main function to be called to perform search engine highlight
 
 204  * Currently it would check for DOM element 'content', element 'container' and
 
 205  * then document.body in that order, so it only highlights appropriate section
 
 206  * on WordPress and Movable Type pages.
 
 208 Hilite.hilite = function() {
 
 209     // If 'debug_referrer' then we will use that as our referrer string
 
 211     var q = Hilite.debug_referrer ? Hilite.debug_referrer : document.referrer;
 
 213     q = Hilite.decodeReferrer(q);
 
 214     if (q && ((Hilite.elementid && 
 
 215                (e = document.getElementById(Hilite.elementid))) || 
 
 216               (e = document.body)))
 
 218         Hilite.hiliteElement(e, q);
 
 222 Hilite.walkElements = function(node, depth, textproc) {
 
 223     var skipre = /^(script|style|textarea)/i;
 
 225     while (node && depth > 0) {
 
 227         if (count >= Hilite.max_nodes) {
 
 228             var handler = function() {
 
 229                 Hilite.walkElements(node, depth, textproc);
 
 231             setTimeout(handler, 50);
 
 235         if (node.nodeType == 1) { // ELEMENT_NODE
 
 236             if (!skipre.test(node.tagName) && node.childNodes.length > 0) {
 
 237                 node = node.childNodes[0];
 
 241         } else if (node.nodeType == 3) { // TEXT_NODE
 
 242             node = textproc(node);
 
 245         if (node.nextSibling) {
 
 246             node = node.nextSibling;
 
 249                 node = node.parentNode;
 
 251                 if (node.nextSibling) {
 
 252                     node = node.nextSibling;
 
 260 // Trigger the highlight using the onload handler.
 
 262     if (window.attachEvent) {
 
 263         window.attachEvent('onload', Hilite.hilite);
 
 264     } else if (window.addEventListener) {
 
 265         window.addEventListener('load', Hilite.hilite, false);
 
 267         var __onload = window.onload;
 
 268         window.onload = function() {