PageSourceSearch

https://www.evhybridtech.co.uk/assets/search/js/autoComplete.js

js evhybridtech.co.uk collected 2026-10-06 13:15:45 UTC 20,100 bytes, 641 lines download raw bytes

1(function (global, factory) {
2  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3  typeof define === 'function' && define.amd ? define(factory) :
4  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.autoComplete = factory());
5})(this, (function () { 'use strict';
6
7  function ownKeys(object, enumerableOnly) {
8    var keys = Object.keys(object);
9
10    if (Object.getOwnPropertySymbols) {
11      var symbols = Object.getOwnPropertySymbols(object);
12      enumerableOnly && (symbols = symbols.filter(function (sym) {
13        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
14      })), keys.push.apply(keys, symbols);
15    }
16
17    return keys;
18  }
19
20  function _objectSpread2(target) {
21    for (var i = 1; i < arguments.length; i++) {
22      var source = null != arguments[i] ? arguments[i] : {};
23      i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
24        _defineProperty(target, key, source[key]);
25      }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
26        Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
27      });
28    }
29
30    return target;
31  }
32
33  function _typeof(obj) {
34    "@babel/helpers - typeof";
35
36    return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
37      return typeof obj;
38    } : function (obj) {
39      return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
40    }, _typeof(obj);
41  }
42
43  function _defineProperty(obj, key, value) {
44    if (key in obj) {
45      Object.defineProperty(obj, key, {
46        value: value,
47        enumerable: true,
48        configurable: true,
49        writable: true
50      });
51    } else {
52      obj[key] = value;
53    }
54
55    return obj;
56  }
57
58  function _toConsumableArray(arr) {
59    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
60  }
61
62  function _arrayWithoutHoles(arr) {
63    if (Array.isArray(arr)) return _arrayLikeToArray(arr);
64  }
65
66  function _iterableToArray(iter) {
67    if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
68  }
69
70  function _unsupportedIterableToArray(o, minLen) {
71    if (!o) return;
72    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
73    var n = Object.prototype.toString.call(o).slice(8, -1);
74    if (n === "Object" && o.constructor) n = o.constructor.name;
75    if (n === "Map" || n === "Set") return Array.from(o);
76    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
77  }
78
79  function _arrayLikeToArray(arr, len) {
80    if (len == null || len > arr.length) len = arr.length;
81
82    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
83
84    return arr2;
85  }
86
87  function _nonIterableSpread() {
88    throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
89  }
90
91  function _createForOfIteratorHelper(o, allowArrayLike) {
92    var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
93
94    if (!it) {
95      if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
96        if (it) o = it;
97        var i = 0;
98
99        var F = function () {};
100
101        return {
102          s: F,
103          n: function () {
104            if (i >= o.length) return {
105              done: true
106            };
107            return {
108              done: false,
109              value: o[i++]
110            };
111          },
112          e: function (e) {
113            throw e;
114          },
115          f: F
116        };
117      }
118
119      throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
120    }
121
122    var normalCompletion = true,
123        didErr = false,
124        err;
125    return {
126      s: function () {
127        it = it.call(o);
128      },
129      n: function () {
130        var step = it.next();
131        normalCompletion = step.done;
132        return step;
133      },
134      e: function (e) {
135        didErr = true;
136        err = e;
137      },
138      f: function () {
139        try {
140          if (!normalCompletion && it.return != null) it.return();
141        } finally {
142          if (didErr) throw err;
143        }
144      }
145    };
146  }
147
148  var select$1 = function select(element) {
149    return typeof element === "string" ? document.querySelector(element) : element();
150  };
151  var create = function create(tag, options) {
152    var el = typeof tag === "string" ? document.createElement(tag) : tag;
153    for (var key in options) {
154      var val = options[key];
155      if (key === "inside") {
156        val.append(el);
157      } else if (key === "dest") {
158        select$1(val[0]).insertAdjacentElement(val[1], el);
159      } else if (key === "around") {
160        var ref = val;
161        ref.parentNode.insertBefore(el, ref);
162        el.append(ref);
163        if (ref.getAttribute("autofocus") != null) ref.focus();
164      } else if (key in el) {
165        el[key] = val;
166      } else {
167        el.setAttribute(key, val);
168      }
169    }
170    return el;
171  };
172  var getQuery = function getQuery(field) {
173    return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement ? field.value : field.innerHTML;
174  };
175  var format = function format(value, diacritics) {
vendor: 8,986 bytes, lines 176-445
176    value = String(value).toLowerCase();
177    return diacritics ? value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : value;
178  };
179  var debounce = function debounce(callback, duration) {
180    var timer;
181    return function () {
182      clearTimeout(timer);
183      timer = setTimeout(function () {
184        return callback();
185      }, duration);
186    };
187  };
188  var checkTrigger = function checkTrigger(query, condition, threshold) {
189    return condition ? condition(query) : query.length >= threshold;
190  };
191  var mark = function mark(value, cls) {
192    return create("mark", _objectSpread2({
193      innerHTML: value
194    }, typeof cls === "string" && {
195      "class": cls
196    })).outerHTML;
197  };
198
199  var configure = (function (ctx) {
200    var name = ctx.name,
201        options = ctx.options,
202        resultsList = ctx.resultsList,
203        resultItem = ctx.resultItem;
204    for (var option in options) {
205      if (_typeof(options[option]) === "object") {
206        if (!ctx[option]) ctx[option] = {};
207        for (var subOption in options[option]) {
208          ctx[option][subOption] = options[option][subOption];
209        }
210      } else {
211        ctx[option] = options[option];
212      }
213    }
214    ctx.selector = ctx.selector || "#" + name;
215    resultsList.destination = resultsList.destination || ctx.selector;
216    resultsList.id = resultsList.id || name + "_list_" + ctx.id;
217    resultItem.id = resultItem.id || name + "_result";
218    ctx.input = select$1(ctx.selector);
219  });
220
221  var eventEmitter = (function (name, ctx) {
222    ctx.input.dispatchEvent(new CustomEvent(name, {
223      bubbles: true,
224      detail: ctx.feedback,
225      cancelable: true
226    }));
227  });
228
229  var search = (function (query, record, options) {
230    var _ref = options || {},
231        mode = _ref.mode,
232        diacritics = _ref.diacritics,
233        highlight = _ref.highlight;
234    var nRecord = format(record, diacritics);
235    record = String(record);
236    query = format(query, diacritics);
237    if (mode === "loose") {
238      query = query.replace(/ /g, "");
239      var qLength = query.length;
240      var cursor = 0;
241      var match = Array.from(record).map(function (character, index) {
242        if (cursor < qLength && nRecord[index] === query[cursor]) {
243          character = highlight ? mark(character, highlight) : character;
244          cursor++;
245        }
246        return character;
247      }).join("");
248      if (cursor === qLength) return match;
249    } else {
250      var _match = nRecord.indexOf(query);
251      if (~_match) {
252        query = record.substring(_match, _match + query.length);
253        _match = highlight ? record.replace(query, mark(query, highlight)) : record;
254        return _match;
255      }
256    }
257  });
258
259  var getData = function getData(ctx, query) {
260    return new Promise(function ($return, $error) {
261      var data;
262      data = ctx.data;
263      if (data.cache && data.store) return $return();
264      return new Promise(function ($return, $error) {
265        if (typeof data.src === "function") {
266          return new Promise(function ($return, $error) {
267            if (data.src.constructor.name === "AsyncFunction") {
268              return data.src(query).then($return, $error);
269            }
270            return $return(data.src(query));
271          }).then($return, $error);
272        }
273        return $return(data.src);
274      }).then(function ($await_7) {
275        try {
276          ctx.feedback = data.store = $await_7;
277          eventEmitter("response", ctx);
278          return $return();
279        } catch ($boundEx) {
280          return $error($boundEx);
281        }
282      }, $error);
283    });
284  };
285  var findMatches = function findMatches(query, ctx) {
286    var data = ctx.data,
287        searchEngine = ctx.searchEngine;
288    var matches = [];
289    data.store.forEach(function (value, index) {
290      var find = function find(key) {
291        var record = key ? value[key] : value;
292        var match = typeof searchEngine === "function" ? searchEngine(query, record) : search(query, record, {
293          mode: searchEngine,
294          diacritics: ctx.diacritics,
295          highlight: ctx.resultItem.highlight
296        });
297        if (!match) return;
298        var result = {
299          match: match,
300          value: value
301        };
302        if (key) result.key = key;
303        matches.push(result);
304      };
305      if (data.keys) {
306        var _iterator = _createForOfIteratorHelper(data.keys),
307            _step;
308        try {
309          for (_iterator.s(); !(_step = _iterator.n()).done;) {
310            var key = _step.value;
311            find(key);
312          }
313        } catch (err) {
314          _iterator.e(err);
315        } finally {
316          _iterator.f();
317        }
318      } else {
319        find();
320      }
321    });
322    if (data.filter) matches = data.filter(matches);
323    var results = matches.slice(0, ctx.resultsList.maxResults);
324    ctx.feedback = {
325      query: query,
326      matches: matches,
327      results: results
328    };
329    eventEmitter("results", ctx);
330  };
331
332  var Expand = "aria-expanded";
333  var Active = "aria-activedescendant";
334  var Selected = "aria-selected";
335  var feedback = function feedback(ctx, index) {
336    ctx.feedback.selection = _objectSpread2({
337      index: index
338    }, ctx.feedback.results[index]);
339  };
340  var render = function render(ctx) {
341    var resultsList = ctx.resultsList,
342        list = ctx.list,
343        resultItem = ctx.resultItem,
344        feedback = ctx.feedback;
345    var matches = feedback.matches,
346        results = feedback.results;
347    ctx.cursor = -1;
348    list.innerHTML = "";
349    if (matches.length || resultsList.noResults) {
350      var fragment = new DocumentFragment();
351      results.forEach(function (result, index) {
352        var element = create(resultItem.tag, _objectSpread2({
353          id: "".concat(resultItem.id, "_").concat(index),
354          role: "option",
355          innerHTML: result.match,
356          inside: fragment
357        }, resultItem["class"] && {
358          "class": resultItem["class"]
359        }));
360        if (resultItem.element) resultItem.element(element, result);
361      });
362      list.append(fragment);
363      if (resultsList.element) resultsList.element(list, feedback);
364      open(ctx);
365    } else {
366      close(ctx);
367    }
368  };
369  var open = function open(ctx) {
370    if (ctx.isOpen) return;
371    (ctx.wrapper || ctx.input).setAttribute(Expand, true);
372    ctx.list.removeAttribute("hidden");
373    ctx.isOpen = true;
374    eventEmitter("open", ctx);
375  };
376  var close = function close(ctx) {
377    if (!ctx.isOpen) return;
378    (ctx.wrapper || ctx.input).setAttribute(Expand, false);
379    ctx.input.setAttribute(Active, "");
380    ctx.list.setAttribute("hidden", "");
381    ctx.isOpen = false;
382    eventEmitter("close", ctx);
383  };
384  var goTo = function goTo(index, ctx) {
385    var resultItem = ctx.resultItem;
386    var results = ctx.list.getElementsByTagName(resultItem.tag);
387    var cls = resultItem.selected ? resultItem.selected.split(" ") : false;
388    if (ctx.isOpen && results.length) {
389      var _results$index$classL;
390      var state = ctx.cursor;
391      if (index >= results.length) index = 0;
392      if (index < 0) index = results.length - 1;
393      ctx.cursor = index;
394      if (state > -1) {
395        var _results$state$classL;
396        results[state].removeAttribute(Selected);
397        if (cls) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(cls));
398      }
399      results[index].setAttribute(Selected, true);
400      if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls));
401      ctx.input.setAttribute(Active, results[ctx.cursor].id);
402      ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5;
403      ctx.feedback.cursor = ctx.cursor;
404      feedback(ctx, index);
405      eventEmitter("navigate", ctx);
406    }
407  };
408  var next = function next(ctx) {
409    goTo(ctx.cursor + 1, ctx);
410  };
411  var previous = function previous(ctx) {
412    goTo(ctx.cursor - 1, ctx);
413  };
414  var select = function select(ctx, event, index) {
415    index = index >= 0 ? index : ctx.cursor;
416    if (index < 0) return;
417    ctx.feedback.event = event;
418    feedback(ctx, index);
419    eventEmitter("selection", ctx);
420    close(ctx);
421  };
422  var click = function click(event, ctx) {
423    var itemTag = ctx.resultItem.tag.toUpperCase();
424    var items = Array.from(ctx.list.querySelectorAll(itemTag));
425    var item = event.target.closest(itemTag);
426    if (item && item.nodeName === itemTag) {
427      select(ctx, event, items.indexOf(item));
428    }
429  };
430  var navigate = function navigate(event, ctx) {
431    switch (event.keyCode) {
432      case 40:
433      case 38:
434        event.preventDefault();
435        event.keyCode === 40 ? next(ctx) : previous(ctx);
436        break;
437      case 13:
438        if (!ctx.submit) event.preventDefault();
439        if (ctx.cursor >= 0) select(ctx, event);
440        break;
441      case 9:
442        if (ctx.resultsList.tabSelect && ctx.cursor >= 0) select(ctx, event);
443        break;
444      case 27:
445        ctx.input.value = ""
vendor: 5,476 bytes, lines 445-641
445;
446        close(ctx);
447        break;
448    }
449  };
450
451  function start (ctx, q) {
452    var _this = this;
453    return new Promise(function ($return, $error) {
454      var queryVal, condition;
455      queryVal = q || getQuery(ctx.input);
456      queryVal = ctx.query ? ctx.query(queryVal) : queryVal;
457      condition = checkTrigger(queryVal, ctx.trigger, ctx.threshold);
458      if (condition) {
459        return getData(ctx, queryVal).then(function ($await_2) {
460          try {
461            if (ctx.feedback instanceof Error) return $return();
462            findMatches(queryVal, ctx);
463            if (ctx.resultsList) render(ctx);
464            return $If_1.call(_this);
465          } catch ($boundEx) {
466            return $error($boundEx);
467          }
468        }, $error);
469      } else {
470        close(ctx);
471        return $If_1.call(_this);
472      }
473      function $If_1() {
474        return $return();
475      }
476    });
477  }
478
479  var eventsManager = function eventsManager(events, callback) {
480    for (var element in events) {
481      for (var event in events[element]) {
482        callback(element, event);
483      }
484    }
485  };
486  var addEvents = function addEvents(ctx) {
487    var events = ctx.events;
488    var run = debounce(function () {
489      return start(ctx);
490    }, ctx.debounce);
491    var publicEvents = ctx.events = _objectSpread2({
492      input: _objectSpread2({}, events && events.input)
493    }, ctx.resultsList && {
494      list: events ? _objectSpread2({}, events.list) : {}
495    });
496    var privateEvents = {
497      input: {
498        input: function input() {
499          run();
500        },
501        keydown: function keydown(event) {
502          navigate(event, ctx);
503        },
504        blur: function blur() {
505          close(ctx);
506        }
507      },
508      list: {
509        mousedown: function mousedown(event) {
510          event.preventDefault();
511        },
512        click: function click$1(event) {
513          click(event, ctx);
514        }
515      }
516    };
517    eventsManager(privateEvents, function (element, event) {
518      if (!ctx.resultsList && event !== "input") return;
519      if (publicEvents[element][event]) return;
520      publicEvents[element][event] = privateEvents[element][event];
521    });
522    eventsManager(publicEvents, function (element, event) {
523      ctx[element].addEventListener(event, publicEvents[element][event]);
524    });
525  };
526  var removeEvents = function removeEvents(ctx) {
527    eventsManager(ctx.events, function (element, event) {
528      ctx[element].removeEventListener(event, ctx.events[element][event]);
529    });
530  };
531
532  function init (ctx) {
533    var _this = this;
534    return new Promise(function ($return, $error) {
535      var placeHolder, resultsList, parentAttrs;
536      placeHolder = ctx.placeHolder;
537      resultsList = ctx.resultsList;
538      parentAttrs = {
539        role: "combobox",
540        "aria-owns": resultsList.id,
541        "aria-haspopup": true,
542        "aria-expanded": false
543      };
544      create(ctx.input, _objectSpread2(_objectSpread2({
545        "aria-controls": resultsList.id,
546        "aria-autocomplete": "both"
547      }, placeHolder && {
548        placeholder: placeHolder
549      }), !ctx.wrapper && _objectSpread2({}, parentAttrs)));
550      if (ctx.wrapper) ctx.wrapper = create("div", _objectSpread2({
551        around: ctx.input,
552        "class": ctx.name + "_wrapper"
553      }, parentAttrs));
554      if (resultsList) ctx.list = create(resultsList.tag, _objectSpread2({
555        dest: [resultsList.destination, resultsList.position],
556        id: resultsList.id,
557        role: "listbox",
558        hidden: "hidden"
559      }, resultsList["class"] && {
560        "class": resultsList["class"]
561      }));
562      addEvents(ctx);
563      if (ctx.data.cache) {
564        return getData(ctx).then(function ($await_2) {
565          try {
566            return $If_1.call(_this);
567          } catch ($boundEx) {
568            return $error($boundEx);
569          }
570        }, $error);
571      }
572      function $If_1() {
573        eventEmitter("init", ctx);
574        return $return();
575      }
576      return $If_1.call(_this);
577    });
578  }
579
580  function extend (autoComplete) {
581    var prototype = autoComplete.prototype;
582    prototype.init = function () {
583      init(this);
584    };
585    prototype.start = function (query) {
586      start(this, query);
587    };
588    prototype.unInit = function () {
589      if (this.wrapper) {
590        var parentNode = this.wrapper.parentNode;
591        parentNode.insertBefore(this.input, this.wrapper);
592        parentNode.removeChild(this.wrapper);
593      }
594      removeEvents(this);
595    };
596    prototype.open = function () {
597      open(this);
598    };
599    prototype.close = function () {
600      close(this);
601    };
602    prototype.goTo = function (index) {
603      goTo(index, this);
604    };
605    prototype.next = function () {
606      next(this);
607    };
608    prototype.previous = function () {
609      previous(this);
610    };
611    prototype.select = function (index) {
612      select(this, null, index);
613    };
614    prototype.search = function (query, record, options) {
615      return search(query, record, options);
616    };
617  }
618
619  function autoComplete(config) {
620    this.options = config;
621    this.id = autoComplete.instances = (autoComplete.instances || 0) + 1;
622    this.name = "autoComplete";
623    this.wrapper = 1;
624    this.threshold = 1;
625    this.debounce = 0;
626    this.resultsList = {
627      position: "afterend",
628      tag: "ul",
629      maxResults: 5
630    };
631    this.resultItem = {
632      tag: "li"
633    };
634    configure(this);
635    extend.call(this, autoComplete);
636    init(this);
637  }
638
639  return autoComplete;
640
641}));

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.