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() {