UNPKG

@openui5/sap.ui.demokit

Version:

OpenUI5 UI Library sap.ui.demokit

148 lines (121 loc) 4.23 kB
(function() { /* * Number of styles that are defined in the CSS. * If there are more query terms than styles, the same style will be applied to each 'N_STYLES'th term. */ var N_STYLES = 3; /* * Search the query parameters of the document URL and the referrer URL for Google-like query terms * (parameter name 'q'). If found, create a regular expression to find occurrences of any of the terms * and a map of CSS classes to be applied to each match. The map uses the lower case version of each term as key. * * @returns {{regexp:RegExp, classes:object}} info object */ function makeQueryInfo() { var rparam = /[^?#]*\?([^#]*&)?q=([^&#]*)(&|#|$)/, rquote = /[.?*+|(){}\[\]\\]/g, match = rparam.exec(document.URL) || rparam.exec(document.referrer), qterms = match && decodeURIComponent(match[2].replace(/\+/g, " ")).split(/\s+/g), classes,i,t; if ( qterms ) { // create a styles map and regexp from the non-empty query terms classes = {}; for (i = 0; i < qterms.length; ) { t = qterms[i].toLowerCase(); if ( t && !classes.hasOwnProperty(t) ) { // ignore duplicates and empty terms classes[t] = 'queryterm' + (1 + i % N_STYLES); qterms[i] = t.replace(rquote, "\\$&"); // quote special regex chars; i++; } else { // remove empty or redundant search terms qterms.splice(i, 1); } } // if there are terms, return an info object with regex and style map if ( qterms.length > 0 ) { return { regexp: new RegExp(qterms.join("|"), 'gi'), classes: classes }; } } // return undefined } /* * Execute the given callback once the DOM is ready (which might already be the case). */ function whenReady(callback) { function onLoaded() { document.removeEventListener( "DOMContentLoaded", onLoaded, false ); callback(); } if ( document.readyState === 'complete' ) { callback(); } else { document.addEventListener( "DOMContentLoaded", onLoaded, false ); } } /* * Loops over all text nodes in the given subtree and searches for text fragments * that match the given RegExp. Each match is wrapped in its own span and the * span is given the class corresponding to the match. */ function markText(node, expr, classes) { /* Names of Element nodes whose children must not be visited */ var rtags = /object|embed|script|select|textarea/i; function replace(node) { var text = node.nodeValue, p = node.parentNode, start = 0, match,span; while ( (match = expr.exec(text)) != null ) { // add a new text node with the string before the match if ( start < match.index ) { p.insertBefore(document.createTextNode(text.slice(start, match.index)), node); } // add a span for the match span = document.createElement("span"); span.appendChild(document.createTextNode(match[0])); span.className = classes[match[0].toLowerCase()]; p.insertBefore(span, node); // robustness: should a non-empty search term result in an empty match, then exit the loop if ( start <= expr.lastindex ) { break; } // continue search after current match start = expr.lastIndex; } // if there was any match, then reduce the text of original text node // to the substring after the last match (might be empty) if ( start > 0 ) { node.nodeValue = text.slice(start); } } function visit(node) { if (node.nodeType == 3) { // Node.TEXT_NODE replace(node); } else if ( !rtags.test(node.nodeName) ) { // skip 'critical' nodes for (node = node.firstChild; node; node = node.nextSibling) { visit(node); } } } visit(node); } // avoid multiple executions if ( document.documentElement.getAttribute('data-highlight-query-terms') ) { return; } document.documentElement.setAttribute('data-highlight-query-terms', 'pending'); /* * Check if there are query terms given. * If so, mark their occurrences in the body of this page. */ var oQueryInfo = makeQueryInfo(); if ( oQueryInfo ) { whenReady(function() { markText(document.body, oQueryInfo.regexp, oQueryInfo.classes); document.documentElement.setAttribute('data-highlight-query-terms', 'done'); }); } }());