diff --git a/static/resources_list.txt b/static/resources_list.txt
index e339a31a4..b9ecb268e 100644
--- a/static/resources_list.txt
+++ b/static/resources_list.txt
@@ -9,7 +9,7 @@ skin/search-icon.svg
skin/iso6391To3.js
skin/isotope.pkgd.min.js
skin/index.js
-skin/autoComplete.min.js
+skin/autoComplete/autoComplete.min.js
skin/kiwix.css
skin/taskbar.css
skin/index.css
@@ -43,7 +43,7 @@ templates/no_js_download.html
opensearchdescription.xml
ft_opensearchdescription.xml
catalog_v2_searchdescription.xml
-skin/css/autoComplete.css
+skin/autoComplete/css/autoComplete.css
skin/favicon/android-chrome-192x192.png
skin/favicon/android-chrome-512x512.png
skin/favicon/apple-touch-icon.png
diff --git a/static/skin/autoComplete/LICENSE b/static/skin/autoComplete/LICENSE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/static/skin/autoComplete/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/static/skin/autoComplete/autoComplete.js b/static/skin/autoComplete/autoComplete.js
new file mode 100644
index 000000000..ec025c50a
--- /dev/null
+++ b/static/skin/autoComplete/autoComplete.js
@@ -0,0 +1,654 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.autoComplete = factory());
+}(this, (function () { 'use strict';
+
+ function ownKeys(object, enumerableOnly) {
+ var keys = Object.keys(object);
+
+ if (Object.getOwnPropertySymbols) {
+ var symbols = Object.getOwnPropertySymbols(object);
+
+ if (enumerableOnly) {
+ symbols = symbols.filter(function (sym) {
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
+ });
+ }
+
+ keys.push.apply(keys, symbols);
+ }
+
+ return keys;
+ }
+
+ function _objectSpread2(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i] != null ? arguments[i] : {};
+
+ if (i % 2) {
+ ownKeys(Object(source), true).forEach(function (key) {
+ _defineProperty(target, key, source[key]);
+ });
+ } else if (Object.getOwnPropertyDescriptors) {
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
+ } else {
+ ownKeys(Object(source)).forEach(function (key) {
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
+ });
+ }
+ }
+
+ return target;
+ }
+
+ function _typeof(obj) {
+ "@babel/helpers - typeof";
+
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function (obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof = function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+ }
+
+ return _typeof(obj);
+ }
+
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+ }
+
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
+ }
+
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) return _arrayLikeToArray(arr);
+ }
+
+ function _iterableToArray(iter) {
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
+ }
+
+ function _unsupportedIterableToArray(o, minLen) {
+ if (!o) return;
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
+ var n = Object.prototype.toString.call(o).slice(8, -1);
+ if (n === "Object" && o.constructor) n = o.constructor.name;
+ if (n === "Map" || n === "Set") return Array.from(o);
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
+ }
+
+ function _arrayLikeToArray(arr, len) {
+ if (len == null || len > arr.length) len = arr.length;
+
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
+
+ return arr2;
+ }
+
+ function _nonIterableSpread() {
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+ }
+
+ function _createForOfIteratorHelper(o, allowArrayLike) {
+ var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
+
+ if (!it) {
+ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
+ if (it) o = it;
+ var i = 0;
+
+ var F = function () {};
+
+ return {
+ s: F,
+ n: function () {
+ if (i >= o.length) return {
+ done: true
+ };
+ return {
+ done: false,
+ value: o[i++]
+ };
+ },
+ e: function (e) {
+ throw e;
+ },
+ f: F
+ };
+ }
+
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+ }
+
+ var normalCompletion = true,
+ didErr = false,
+ err;
+ return {
+ s: function () {
+ it = it.call(o);
+ },
+ n: function () {
+ var step = it.next();
+ normalCompletion = step.done;
+ return step;
+ },
+ e: function (e) {
+ didErr = true;
+ err = e;
+ },
+ f: function () {
+ try {
+ if (!normalCompletion && it.return != null) it.return();
+ } finally {
+ if (didErr) throw err;
+ }
+ }
+ };
+ }
+
+ var select$1 = function select(element) {
+ return typeof element === "string" ? document.querySelector(element) : element();
+ };
+ var create = function create(tag, options) {
+ var el = typeof tag === "string" ? document.createElement(tag) : tag;
+ for (var key in options) {
+ var val = options[key];
+ if (key === "inside") {
+ val.append(el);
+ } else if (key === "dest") {
+ select$1(val[0]).insertAdjacentElement(val[1], el);
+ } else if (key === "around") {
+ var ref = val;
+ ref.parentNode.insertBefore(el, ref);
+ el.append(ref);
+ if (ref.getAttribute("autofocus") != null) ref.focus();
+ } else if (key in el) {
+ el[key] = val;
+ } else {
+ el.setAttribute(key, val);
+ }
+ }
+ return el;
+ };
+ var getQuery = function getQuery(field) {
+ return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement ? field.value : field.innerHTML;
+ };
+ var format = function format(value, diacritics) {
+ value = value.toString().toLowerCase();
+ return diacritics ? value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : value;
+ };
+ var debounce = function debounce(callback, duration) {
+ var timer;
+ return function () {
+ clearTimeout(timer);
+ timer = setTimeout(function () {
+ return callback();
+ }, duration);
+ };
+ };
+ var checkTrigger = function checkTrigger(query, condition, threshold) {
+ return condition ? condition(query) : query.length >= threshold;
+ };
+ var mark = function mark(value, cls) {
+ return create("mark", _objectSpread2({
+ innerHTML: value
+ }, typeof cls === "string" && {
+ "class": cls
+ })).outerHTML;
+ };
+
+ var configure = (function (ctx) {
+ var name = ctx.name,
+ options = ctx.options,
+ resultsList = ctx.resultsList,
+ resultItem = ctx.resultItem;
+ for (var option in options) {
+ if (_typeof(options[option]) === "object") {
+ if (!ctx[option]) ctx[option] = {};
+ for (var subOption in options[option]) {
+ ctx[option][subOption] = options[option][subOption];
+ }
+ } else {
+ ctx[option] = options[option];
+ }
+ }
+ ctx.selector = ctx.selector || "#" + name;
+ resultsList.destination = resultsList.destination || ctx.selector;
+ resultsList.id = resultsList.id || name + "_list_" + ctx.id;
+ resultItem.id = resultItem.id || name + "_result";
+ ctx.input = select$1(ctx.selector);
+ });
+
+ var eventEmitter = (function (name, ctx) {
+ ctx.input.dispatchEvent(new CustomEvent(name, {
+ bubbles: true,
+ detail: ctx.feedback,
+ cancelable: true
+ }));
+ });
+
+ var search = (function (query, record, options) {
+ var _ref = options || {},
+ mode = _ref.mode,
+ diacritics = _ref.diacritics,
+ highlight = _ref.highlight;
+ var nRecord = format(record, diacritics);
+ record = record.toString();
+ query = format(query, diacritics);
+ if (mode === "loose") {
+ query = query.replace(/ /g, "");
+ var qLength = query.length;
+ var cursor = 0;
+ var match = Array.from(record).map(function (character, index) {
+ if (cursor < qLength && nRecord[index] === query[cursor]) {
+ character = highlight ? mark(character, highlight) : character;
+ cursor++;
+ }
+ return character;
+ }).join("");
+ if (cursor === qLength) return match;
+ } else {
+ var _match = nRecord.indexOf(query);
+ if (~_match) {
+ query = record.substring(_match, _match + query.length);
+ _match = highlight ? record.replace(query, mark(query, highlight)) : record;
+ return _match;
+ }
+ }
+ });
+
+ var getData = function getData(ctx, query) {
+ return new Promise(function ($return, $error) {
+ var data;
+ data = ctx.data;
+ if (data.cache && data.store) return $return();
+ return new Promise(function ($return, $error) {
+ if (typeof data.src === "function") {
+ return data.src(query).then($return, $error);
+ }
+ return $return(data.src);
+ }).then(function ($await_4) {
+ try {
+ ctx.feedback = data.store = $await_4;
+ eventEmitter("response", ctx);
+ return $return();
+ } catch ($boundEx) {
+ return $error($boundEx);
+ }
+ }, $error);
+ });
+ };
+ var findMatches = function findMatches(query, ctx) {
+ var data = ctx.data,
+ searchEngine = ctx.searchEngine;
+ var matches = [];
+ data.store.forEach(function (value, index) {
+ var find = function find(key) {
+ var record = key ? value[key] : value;
+ var match = typeof searchEngine === "function" ? searchEngine(query, record) : search(query, record, {
+ mode: searchEngine,
+ diacritics: ctx.diacritics,
+ highlight: ctx.resultItem.highlight
+ });
+ if (!match) return;
+ var result = {
+ match: match,
+ value: value
+ };
+ if (key) result.key = key;
+ matches.push(result);
+ };
+ if (data.keys) {
+ var _iterator = _createForOfIteratorHelper(data.keys),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var key = _step.value;
+ find(key);
+ }
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ } else {
+ find();
+ }
+ });
+ if (data.filter) matches = data.filter(matches);
+ var results = matches.slice(0, ctx.resultsList.maxResults);
+ ctx.feedback = {
+ query: query,
+ matches: matches,
+ results: results
+ };
+ eventEmitter("results", ctx);
+ };
+
+ var Expand = "aria-expanded";
+ var Active = "aria-activedescendant";
+ var Selected = "aria-selected";
+ var feedback = function feedback(ctx, index) {
+ ctx.feedback.selection = _objectSpread2({
+ index: index
+ }, ctx.feedback.results[index]);
+ };
+ var render = function render(ctx) {
+ var resultsList = ctx.resultsList,
+ list = ctx.list,
+ resultItem = ctx.resultItem,
+ feedback = ctx.feedback;
+ var matches = feedback.matches,
+ results = feedback.results;
+ ctx.cursor = -1;
+ list.innerHTML = "";
+ if (matches.length || resultsList.noResults) {
+ var fragment = new DocumentFragment();
+ results.forEach(function (result, index) {
+ var element = create(resultItem.tag, _objectSpread2({
+ id: "".concat(resultItem.id, "_").concat(index),
+ role: "option",
+ innerHTML: result.match,
+ inside: fragment
+ }, resultItem["class"] && {
+ "class": resultItem["class"]
+ }));
+ if (resultItem.element) resultItem.element(element, result);
+ });
+ list.append(fragment);
+ if (resultsList.element) resultsList.element(list, feedback);
+ open(ctx);
+ } else {
+ close(ctx);
+ }
+ };
+ var open = function open(ctx) {
+ if (ctx.isOpen) return;
+ (ctx.wrapper || ctx.input).setAttribute(Expand, true);
+ ctx.list.removeAttribute("hidden");
+ ctx.isOpen = true;
+ eventEmitter("open", ctx);
+ };
+ var close = function close(ctx) {
+ if (!ctx.isOpen) return;
+ (ctx.wrapper || ctx.input).setAttribute(Expand, false);
+ ctx.input.setAttribute(Active, "");
+ ctx.list.setAttribute("hidden", "");
+ ctx.isOpen = false;
+ eventEmitter("close", ctx);
+ };
+ var goTo = function goTo(index, ctx) {
+ var resultItem = ctx.resultItem;
+ var results = ctx.list.getElementsByTagName(resultItem.tag);
+ var cls = resultItem.selected ? resultItem.selected.split(" ") : false;
+ if (ctx.isOpen && results.length) {
+ var _results$index$classL;
+ var state = ctx.cursor;
+ if (index >= results.length) index = 0;
+ if (index < 0) index = results.length - 1;
+ ctx.cursor = index;
+ if (state > -1) {
+ var _results$state$classL;
+ results[state].removeAttribute(Selected);
+ if (cls) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(cls));
+ }
+ results[index].setAttribute(Selected, true);
+ if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls));
+ ctx.input.setAttribute(Active, results[ctx.cursor].id);
+ ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5;
+ ctx.feedback.cursor = ctx.cursor;
+ feedback(ctx, index);
+ eventEmitter("navigate", ctx);
+ }
+ };
+ var next = function next(ctx) {
+ goTo(ctx.cursor + 1, ctx);
+ };
+ var previous = function previous(ctx) {
+ goTo(ctx.cursor - 1, ctx);
+ };
+ var select = function select(ctx, event, index) {
+ index = index >= 0 ? index : ctx.cursor;
+ if (index < 0) return;
+ ctx.feedback.event = event;
+ feedback(ctx, index);
+ eventEmitter("selection", ctx);
+ close(ctx);
+ };
+ var click = function click(event, ctx) {
+ var itemTag = ctx.resultItem.tag.toUpperCase();
+ var items = Array.from(ctx.list.querySelectorAll(itemTag));
+ var item = event.target.closest(itemTag);
+ if (item && item.nodeName === itemTag) {
+ select(ctx, event, items.indexOf(item));
+ }
+ };
+ var navigate = function navigate(event, ctx) {
+ switch (event.keyCode) {
+ case 40:
+ case 38:
+ event.preventDefault();
+ event.keyCode === 40 ? next(ctx) : previous(ctx);
+ break;
+ case 13:
+ if (!ctx.submit) event.preventDefault();
+ if (ctx.cursor >= 0) select(ctx, event);
+ break;
+ case 9:
+ if (ctx.resultsList.tabSelect && ctx.cursor >= 0) select(ctx, event);
+ break;
+ case 27:
+ ctx.input.value = "";
+ close(ctx);
+ break;
+ }
+ };
+
+ function start (ctx, q) {
+ var _this = this;
+ return new Promise(function ($return, $error) {
+ var queryVal, condition;
+ queryVal = q || getQuery(ctx.input);
+ queryVal = ctx.query ? ctx.query(queryVal) : queryVal;
+ condition = checkTrigger(queryVal, ctx.trigger, ctx.threshold);
+ if (condition) {
+ return getData(ctx, queryVal).then(function ($await_2) {
+ try {
+ if (ctx.feedback instanceof Error) return $return();
+ findMatches(queryVal, ctx);
+ if (ctx.resultsList) render(ctx);
+ return $If_1.call(_this);
+ } catch ($boundEx) {
+ return $error($boundEx);
+ }
+ }, $error);
+ } else {
+ close(ctx);
+ return $If_1.call(_this);
+ }
+ function $If_1() {
+ return $return();
+ }
+ });
+ }
+
+ var eventsManager = function eventsManager(events, callback) {
+ for (var element in events) {
+ for (var event in events[element]) {
+ callback(element, event);
+ }
+ }
+ };
+ var addEvents = function addEvents(ctx) {
+ var events = ctx.events;
+ var run = debounce(function () {
+ return start(ctx);
+ }, ctx.debounce);
+ var publicEvents = ctx.events = _objectSpread2({
+ input: _objectSpread2({}, events && events.input)
+ }, ctx.resultsList && {
+ list: events ? _objectSpread2({}, events.list) : {}
+ });
+ var privateEvents = {
+ input: {
+ input: function input() {
+ run();
+ },
+ keydown: function keydown(event) {
+ navigate(event, ctx);
+ },
+ blur: function blur() {
+ close(ctx);
+ }
+ },
+ list: {
+ mousedown: function mousedown(event) {
+ event.preventDefault();
+ },
+ click: function click$1(event) {
+ click(event, ctx);
+ }
+ }
+ };
+ eventsManager(privateEvents, function (element, event) {
+ if (!ctx.resultsList && event !== "input") return;
+ if (publicEvents[element][event]) return;
+ publicEvents[element][event] = privateEvents[element][event];
+ });
+ eventsManager(publicEvents, function (element, event) {
+ ctx[element].addEventListener(event, publicEvents[element][event]);
+ });
+ };
+ var removeEvents = function removeEvents(ctx) {
+ eventsManager(ctx.events, function (element, event) {
+ ctx[element].removeEventListener(event, ctx.events[element][event]);
+ });
+ };
+
+ function init (ctx) {
+ var _this = this;
+ return new Promise(function ($return, $error) {
+ var placeHolder, resultsList, parentAttrs;
+ placeHolder = ctx.placeHolder;
+ resultsList = ctx.resultsList;
+ parentAttrs = {
+ role: "combobox",
+ "aria-owns": resultsList.id,
+ "aria-haspopup": true,
+ "aria-expanded": false
+ };
+ create(ctx.input, _objectSpread2(_objectSpread2({
+ "aria-controls": resultsList.id,
+ "aria-autocomplete": "both"
+ }, placeHolder && {
+ placeholder: placeHolder
+ }), !ctx.wrapper && _objectSpread2({}, parentAttrs)));
+ if (ctx.wrapper) ctx.wrapper = create("div", _objectSpread2({
+ around: ctx.input,
+ "class": ctx.name + "_wrapper"
+ }, parentAttrs));
+ if (resultsList) ctx.list = create(resultsList.tag, _objectSpread2({
+ dest: [resultsList.destination, resultsList.position],
+ id: resultsList.id,
+ role: "listbox",
+ hidden: "hidden"
+ }, resultsList["class"] && {
+ "class": resultsList["class"]
+ }));
+ addEvents(ctx);
+ if (ctx.data.cache) {
+ return getData(ctx).then(function ($await_2) {
+ try {
+ return $If_1.call(_this);
+ } catch ($boundEx) {
+ return $error($boundEx);
+ }
+ }, $error);
+ }
+ function $If_1() {
+ eventEmitter("init", ctx);
+ return $return();
+ }
+ return $If_1.call(_this);
+ });
+ }
+
+ function extend (autoComplete) {
+ var prototype = autoComplete.prototype;
+ prototype.init = function () {
+ init(this);
+ };
+ prototype.start = function (query) {
+ start(this, query);
+ };
+ prototype.unInit = function () {
+ if (this.wrapper) {
+ var parentNode = this.wrapper.parentNode;
+ parentNode.insertBefore(this.input, this.wrapper);
+ parentNode.removeChild(this.wrapper);
+ }
+ removeEvents(this);
+ };
+ prototype.open = function () {
+ open(this);
+ };
+ prototype.close = function () {
+ close(this);
+ };
+ prototype.goTo = function (index) {
+ goTo(index, this);
+ };
+ prototype.next = function () {
+ next(this);
+ };
+ prototype.previous = function () {
+ previous(this);
+ };
+ prototype.select = function (index) {
+ select(this, null, index);
+ };
+ prototype.search = function (query, record, options) {
+ return search(query, record, options);
+ };
+ }
+
+ function autoComplete(config) {
+ this.options = config;
+ this.id = autoComplete.instances = (autoComplete.instances || 0) + 1;
+ this.name = "autoComplete";
+ this.wrapper = 1;
+ this.threshold = 1;
+ this.debounce = 0;
+ this.resultsList = {
+ position: "afterend",
+ tag: "ul",
+ maxResults: 5
+ };
+ this.resultItem = {
+ tag: "li"
+ };
+ configure(this);
+ extend.call(this, autoComplete);
+ init(this);
+ }
+
+ return autoComplete;
+
+})));
diff --git a/static/skin/autoComplete.min.js b/static/skin/autoComplete/autoComplete.min.js
similarity index 100%
rename from static/skin/autoComplete.min.js
rename to static/skin/autoComplete/autoComplete.min.js
diff --git a/static/skin/css/autoComplete.css b/static/skin/autoComplete/css/autoComplete.css
similarity index 95%
rename from static/skin/css/autoComplete.css
rename to static/skin/autoComplete/css/autoComplete.css
index 19267485c..2434d6164 100644
--- a/static/skin/css/autoComplete.css
+++ b/static/skin/autoComplete/css/autoComplete.css
@@ -1,3 +1,4 @@
+/* Modified from https://github.com/TarekRaafat/autoComplete.js (version 10.2.6)*/
.autoComplete_wrapper {
display: inline-block;
position: relative;
diff --git a/static/viewer.html b/static/viewer.html
index 49899723a..99a16c22d 100644
--- a/static/viewer.html
+++ b/static/viewer.html
@@ -10,12 +10,12 @@
-
+
-
+
-
+
const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032";
src="./skin/langSelector.svg?cacheid=00b59961">
@@ -379,7 +379,7 @@ const char* urls404[] = {
"/ROOT%23%3",
"/ROOT%23%3Fxyz",
"/ROOT%23%3F/skin/non-existent-skin-resource",
- "/ROOT%23%3F/skin/autoComplete.min.js?cacheid=wrongcacheid",
+ "/ROOT%23%3F/skin/autoComplete/autoComplete.min.js?cacheid=wrongcacheid",
"/ROOT%23%3F/catalog",
"/ROOT%23%3F/catalog/",
"/ROOT%23%3F/catalog/non-existent-item",