PageSourceSearch

https://commissionsantementale.ca/wp-content/themes/mhcc/js/jquery.glossarize.js?ver=1.0.0

js commissionsantementale.ca collected 2026-10-03 21:33:02 UTC 9,814 bytes, 363 lines download raw bytes

1/**
2 * Plugin Name: Glossarizer
3 * Author : Vinay @Pebbleroad
4 */
5
6// Expose plugin as an AMD module if AMD loader is present:
7(function (factory) {
8    "use strict";
9    if (typeof define === 'function' && define.amd) {
10        // AMD. Register as an anonymous module.
11        define(['jquery'], factory);
12    } else if (typeof exports === 'object' && typeof require === 'function') {
13        // Browserify
14        factory(require('jquery'));
15    } else {
16        // Browser globals
17        factory(jQuery);
18    }
19}(function ($) {
20    'use strict';
21  /**
22   * Defaults
23   */
24
25  var pluginName = 'glossarizer',
26    defaults = {
27      sourceURL: '', /* URL of the JSON file with format {"term": "", "description": ""} */
28      replaceTag: 'abbr', /* Matching words will be wrapped with abbr tags by default */
29      lookupTagName: 'p, ul, a, div', /* Lookup in either paragraphs or lists. Do not replace in headings */
30      callback: null, /* Callback once all tags are replaced: Call or tooltip or anything you like */
31      replaceOnce: false, /* Replace only once in a TextNode */
32      replaceClass: 'glossarizer_replaced',
33      caseSensitive: false,
34      exactMatch: false
35  }
36
37  /**
38   * Constructor
39   */
40
41  function Glossarizer (el, options) {
42    var base = this
43
44    base.el = el
45
46    /* Element */
47    base.$el = $(el)
48
49    /* Extend options */
50
51    base.options = $.extend({}, defaults, options)
52
53    /* Terms */
54
55    base.terms = []
56
57    /* Excludes array */
58
59    base.excludes = []
60
61    /* Replaced words array */
62
63    base.replaced = []
64
65    /* Regex Tags */
66
67    base.regexOption = (base.options.caseSensitive ? '' : 'i') + (base.options.replaceOnce ? '' : 'g')
68
69    /* Fetch glossary JSON */
70      base.glossary = this.options.sourceURL
71
72      if (!base.glossary.length || base.glossary.length == 0) return
73
74      /**
75       * Get all terms
76       */
77
78      for (var i = 0; i < base.glossary.length; i++) {
79        var terms = base.glossary[i].term.split(',')
80
81        for (var j = 0; j < terms.length; j++) {
82          /* Trim */
83
84          var trimmed = terms[j].replace(/^\s+|\s+$/g, ''),
85            isExclusion = trimmed.indexOf('!')
86
87          if (isExclusion == -1 || isExclusion != 0) {
88            /* Glossary terms array */
89
90            base.terms.push(trimmed)
91          } else {
92            /* Excluded terms array */
93
94            base.excludes.push(trimmed.substr(1))
95          }
96        }
97      }
98
99      /**
100       * Wrap terms
101       */
102
103      base.wrapTerms()
104  }
105
106  /**
107   * Prototypes
108   */
109  Glossarizer.prototype = {
110    getDescription: function (term) {
111      var regex = new RegExp('(\,|\s*)' + this.clean(term) + '\\s*|\\,$', 'i')
112
113      /**
114       * Matches
115       * 1. Starts with \s* (zero or more spaces)
116       * 2. Ends with zero or more spaces
117       * 3. Ends with comma
118       */
119
120      for (var i = 0; i < this.glossary.length; i++) {
121        if (this.options.exactMatch) {
122          if (this.glossary[i].term == this.clean(term)) {
123            return this.glossary[i].description.replace(/\"/gi, '&quot;')
124          }
125        } else {
126          if (this.glossary[i].term.match(regex)) {
127            return this.glossary[i].description.replace(/\"/gi, '&quot;')
128          }
129        }
130      }
131    },
132    getLink: function (term) {
133      var regex = new RegExp('(\,|\s*)' + this.clean(term) + '\\s*|\\,$', 'i')
134
135      /**
136       * Matches
137       * 1. Starts with \s* (zero or more spaces)
138       * 2. Ends with zero or more spaces
139       * 3. Ends with comma
140       */
141
142      for (var i = 0; i < this.glossary.length; i++) {
143        if (this.options.exactMatch) {
144          if (this.glossary[i].term == this.clean(term)) {
145            return this.glossary[i].link.replace(/\"/gi, '&quot;')
146          }
147        } else {
148          if (this.glossary[i].term.match(regex)) {
149            return this.glossary[i].link.replace(/\"/gi, '&quot;')
150          }
151        }
152      }
153    },
154    getSlug: function (term) {
155      var regex = new RegExp('(\,|\s*)' + this.clean(term) + '\\s*|\\,$', 'i')
156
157      /**
158       * Matches
159       * 1. Starts with \s* (zero or more spaces)
160       * 2. Ends with zero or more spaces
161       * 3. Ends with comma
162       */
163
164      for (var i = 0; i < this.glossary.length; i++) {
165        if (this.options.exactMatch) {
166          if (this.glossary[i].term == this.clean(term)) {
167            return this.glossary[i].slug.replace(/\"/gi, '&quot;')
168          }
169        } else {
170          if (this.glossary[i].term.match(regex)) {
171            return this.glossary[i].slug.replace(/\"/gi, '&quot;')
172          }
173        }
174      }
175    },
176    clean: function (text) {
177      var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g')
178
179      return text.replace(reEscape, '\\$1')
180    },
181
182    /**
183     * Wraps the matched terms by calling traverser
184     */
185    wrapTerms: function () {
186      this.cleanedTerms = this.clean(this.terms.join('|'))
187      this.excludedTerms = this.clean(this.excludes.join('|'))
188
189      var nodes = this.el.querySelectorAll(this.options.lookupTagName)
190
191      for (var i = 0; i < nodes.length; i++) {
192        this.traverser(nodes[i])
193      }
194
195      /**
196       * Callback
197       */
198
199      if (this.options.callback) this.options.callback.call(this.$el)
200    },
201
202    /**
203     * Traverses through nodes to find the matching terms in TEXTNODES
204     */
205
206    traverser: function (node) {
207      var next,
208        base = this
209
210      if (node.nodeType === 1) {
211        /*
212         Element Node
213         */
214
215        if (node = node.firstChild) {
216          do {
217            // Recursively call traverseChildNodes
218            // on each child node
219            next = node.nextSibling
220
221            /**
222             * Check if the node is not glossarized
223             */
224
225            if (node.className != this.options.replaceClass) {
226              this.traverser(node)
227            }
228          } while (node = next)
229        }
230      } else if (node.nodeType === 3) {
231        /*
232         Text Node
233         */
234
235        var temp = document.createElement('div'),
236          data = node.data
237
238        var re = new RegExp('(?:^|\\b)(' + this.cleanedTerms + ')(?!\\w)', base.regexOption),
239          reEx = new RegExp('(?:^|\\b)(' + this.excludedTerms + ')(?!\\w)', base.regexOption)
240
241        if (re.test(data)) {
242          var excl = reEx.exec(data)
243
244          data = data.replace(re, function (match, item , offset, string) {
245            if (base.options.replaceOnce && inArrayIn(match, base.replaced) >= 0) {
246              return match
247            }
248
249            base.replaced.push(match)
250
251            var ir = new RegExp('(?:^|\\b)' + base.clean(match) + '(?!\\w)'),
252              result = ir.exec(data)
253
254            if (result) {
255              if (excl && base.excludes.length) {
256                var id = offset,
257                  exid = excl.index,
258                  exl = excl.index + excl[0].length
259
260                if (exid <= id && id <= exl) {
261                  return match
262                } else {
263                  return '<' + base.options.replaceTag + ' href="' + base.getLink(match) +  '" aria-describedby="' + base.getSlug(match) + '" class="' + base.options.replaceClass + '">' + match + '<span class="c-glossTerm__tip" role="tooltip" id="' + base.getSlug(match) + '">' + base.getDescription(match) + '</span></' + base.options.replaceTag + '>'
264                }
265              } else {
266                return '<' + base.options.replaceTag + ' href="' + base.getLink(match) +  '" aria-describedby="' + base.getSlug(match) + '" class="' + base.options.replaceClass + '">' + match + '<span class="c-glossTerm__tip" role="tooltip" id="' + base.getSlug(match) + '">' + base.getDescription(match) + '</span></' + base.options.replaceTag + '>'
267              }
268            }
269          })
270
271          /**
272           * Only replace when a match is found
273           * Resorting to jQuery html() because of IE8 whitespace issue.
274           * IE 8 removes leading whitespace from Text Nodes. Hence innerhtml doesnt work.
275           *
276           */
277
278          $(temp).html(data)
279
280          while (temp.firstChild) {
281            node.parentNode.insertBefore(temp.firstChild, node)
282          }
283
284          node.parentNode.removeChild(node)
285        }
286      }
287    },
288
289  }
290
291  /**
292   * Public Methods
293   */
294
295  var methods = {
296    destroy: function () {
297      this.$el.removeData('plugin_' + pluginName)
298
299      /* Remove abbr tag */
300      this.$el.find('.' + this.options.replaceClass).each(function () {
301        var $this = $(this),
302          text = $this.text()
303
304        $this.replaceWith(text)
305      })
306    }
307  }
308
309  /**
310   * Extend Prototype
311   */
312
313  Glossarizer.prototype = $.extend({}, Glossarizer.prototype, methods)
314
315  /**
316   * Plugin
317   * @param  {[type]} options
318   */
319  $.fn[pluginName] = function (options) {
320    return this.each(function () {
321      var $this = $(this),
322        glossarizer = $this.data('plugin_' + pluginName)
323
324      /*
325      Check if its a method
326       */
327
328      if (typeof options == 'string' && glossarizer && methods.hasOwnProperty(options)) {
329        glossarizer[options].apply(glossarizer)
330      } else {
331        /* Destroy if exists */
332
333        if (glossarizer) glossarizer['destroy'].apply(glossarizer)
334
335        /* Initialize */
336
337        $.data(this, 'plugin_' + pluginName, new Glossarizer(this, options))
338      }
339    })
340  }
341
342  /**
343   * In Array
344   */
345
346  function inArrayIn (elem, arr, i) {
347    if (typeof elem !== 'string') {
348      return $.inArray.apply(this, arguments)
349    }
350
351    if (arr) {
352      var len = arr.length
353      i = i ? (i < 0 ? Math.max(0, len + i) : i) : 0
354      elem = elem.toLowerCase()
355      for (; i < len; i++) {
356        if (i in arr && arr[i].toLowerCase() == elem) {
357          return i
358        }
359      }
360    }
361    return -1
362  }
363}));

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.