PageSourceSearch

https://cityofseattle.org/cms_resources/js/jquery.highlight.js

js cityofseattle.org collected 2026-10-07 23:52:08 UTC 6,835 bytes, 214 lines download raw bytes

vendor: 6,835 bytes, lines 1-214
1/*
2 * jQuery Highlight plugin
3 *
4 * Based on highlight v3 by Johann Burkard
5 * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
6 *
7 * Code a little bit refactored and cleaned (in my humble opinion).
8 * Most important changes:
9 *  - has an option to highlight only entire words (wordsOnly - false by default),
10 *  - has an option to be case sensitive (caseSensitive - false by default)
11 *  - highlight element tag and class names can be specified in options
12 *
13 * Usage:
14 *   // wrap every occurrence of text 'lorem' in content
15 *   // with <span class='highlight'> (default options)
16 *   $('#content').highlight('lorem');
17 *
18 *   // search for and highlight more terms at once
19 *   // so you can save some time on traversing DOM
20 *   $('#content').highlight(['lorem', 'ipsum']);
21 *   $('#content').highlight('lorem ipsum');
22 *
23 *   // search only for entire word 'lorem'
24 *   $('#content').highlight('lorem', { wordsOnly: true });
25 *
26 *   // search only for the entire word 'C#'
27 *   // and make sure that the word boundary can also
28 *   // be a 'non-word' character, as well as a regex latin1 only boundary:
29 *   $('#content').highlight('C#', { wordsOnly: true , wordsBoundary: '[\\b\\W]' });
30 *
31 *   // don't ignore case during search of term 'lorem'
32 *   $('#content').highlight('lorem', { caseSensitive: true });
33 *
34 *   // wrap every occurrence of term 'ipsum' in content
35 *   // with <em class='important'>
36 *   $('#content').highlight('ipsum', { element: 'em', className: 'important' });
37 *
38 *   // remove default highlight
39 *   $('#content').unhighlight();
40 *
41 *   // remove custom highlight
42 *   $('#content').unhighlight({ element: 'em', className: 'important' });
43 *
44 *
45 * Copyright (c) 2009 Bartek Szopka
46 *
47 * Licensed under MIT license.
48 *
49 */
50
51(function(factory) {
52  if (typeof define === 'function' && define.amd) {
53    // AMD. Register as an anonymous module.
54    define(['jquery'], factory);
55  } else if (typeof exports === 'object') {
56    // Node/CommonJS
57    factory(require('jquery'));
58  } else {
59    // Browser globals
60    factory(jQuery);
61  }
62})(function(jQuery) {
63  jQuery.extend({
64    highlight: function(
65      node,
66      re,
67      nodeName,
68      className,
69      callback,
70      ignoreDiacritics
71    ) {
72      if (node.nodeType === 3) {
73        var subject = ignoreDiacritics
74          ? jQuery.removeDiacritcs(node.data)
75          : node.data;
76        var match = subject.match(re);
77        if (match) {
78          // The new highlight Element Node
79          var highlight = document.createElement(nodeName || 'span');
80          highlight.className = className || 'highlight';
81          // Note that we use the captured value to find the real index
82          // of the match. This is because we do not want to include the matching word boundaries
83          var capturePos = node.data.indexOf(match[1], match.index);
84
85          // Split the node and replace the matching wordnode
86          // with the highlighted node
87          var wordNode = node.splitText(capturePos);
88          wordNode.splitText(match[1].length);
89
90          var wordClone = wordNode.cloneNode(true);
91          highlight.appendChild(wordClone);
92          wordNode.parentNode.replaceChild(highlight, wordNode);
93          if (typeof callback == 'function') {
94            callback(highlight);
95          }
96          return 1; //skip added node in parent
97        }
98      } else if (
99        node.nodeType === 1 &&
100        node.childNodes && // only element nodes that have children
101        !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
102        !(
103          node.tagName === nodeName.toUpperCase() &&
104          node.className === className
105        )
106      ) {
107        // skip if already highlighted
108        for (var i = 0; i < node.childNodes.length; i++) {
109          i += jQuery.highlight(
110            node.childNodes[i],
111            re,
112            nodeName,
113            className,
114            callback,
115            ignoreDiacritics
116          );
117        }
118      }
119      return 0;
120    },
121
122    removeDiacritcs: function(word) {
123      return word
124        .replace(/[\u00c0-\u00c6]/g, 'A')
125        .replace(/[\u00e0-\u00e6]/g, 'a')
126        .replace(/[\u00c7]/g, 'C')
127        .replace(/[\u00e7]/g, 'c')
128        .replace(/[\u00c8-\u00cb]/g, 'E')
129        .replace(/[\u00e8-\u00eb]/g, 'e')
130        .replace(/[\u00cc-\u00cf]/g, 'I')
131        .replace(/[\u00ec-\u00ef]/g, 'i')
132        .replace(/[\u00d1|\u0147]/g, 'N')
133        .replace(/[\u00f1|\u0148]/g, 'n')
134        .replace(/[\u00d2-\u00d8|\u0150]/g, 'O')
135        .replace(/[\u00f2-\u00f8|\u0151]/g, 'o')
136        .replace(/[\u0160]/g, 'S')
137        .replace(/[\u0161]/g, 's')
138        .replace(/[\u00d9-\u00dc]/g, 'U')
139        .replace(/[\u00f9-\u00fc]/g, 'u')
140        .replace(/[\u00dd]/g, 'Y')
141        .replace(/[\u00fd]/g, 'y');
142    }
143  });
144
145  jQuery.fn.unhighlight = function(options) {
146    var settings = {
147      className: 'highlight',
148      element: 'span'
149    };
150
151    jQuery.extend(settings, options);
152
153    return this.find(settings.element + '.' + settings.className)
154      .each(function() {
155        var parent = this.parentNode;
156        parent.replaceChild(this.firstChild, this);
157        parent.normalize();
158      })
159      .end();
160  };
161
162  jQuery.fn.highlight = function(words, options, callback) {
163    var settings = {
164      className: 'highlight',
165      element: 'span',
166      caseSensitive: false,
167      wordsOnly: false,
168      wordsBoundary: '\\b',
169      ignoreDiacritics: false
170    };
171
172    jQuery.extend(settings, options);
173
174    if (typeof words === 'string') {
175      words = [words];
176    }
177    words = jQuery.grep(words, function(word) {
178      return word != '';
179    });
180    words = jQuery.map(words, function(word) {
181      if (settings.ignoreDiacritics) {
182        word = jQuery.removeDiacritcs(word);
183      }
184      return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
185    });
186
187    if (words.length === 0) {
188      return this;
189    }
190
191    var flag = settings.caseSensitive ? '' : 'i';
192    // The capture parenthesis will make sure we can match
193    // only the matching word
194    var pattern = '(' + words.join('|') + ')';
195    if (settings.wordsOnly) {
196      pattern =
197        (settings.wordsBoundaryStart || settings.wordsBoundary) +
198        pattern +
199        (settings.wordsBoundaryEnd || settings.wordsBoundary);
200    }
201    var re = new RegExp(pattern, flag);
202
203    return this.each(function() {
204      jQuery.highlight(
205        this,
206        re,
207        settings.element,
208        settings.className,
209        callback,
210        settings.ignoreDiacritics
211      );
212    });
213  };
214});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.