diff options
author | Julian <julian@vantage-design.com> | 2020-02-06 23:20:17 +0300 |
---|---|---|
committer | Julian <julian@vantage-design.com> | 2020-02-06 23:20:17 +0300 |
commit | 5ebb0ab1e5106bdfcafd5b459d466e16a59b6b01 (patch) | |
tree | 8bef96f8c1030cdf872d80d55ec11b2f25360497 /static/plugins/auto-complete.js |
First commit
Diffstat (limited to 'static/plugins/auto-complete.js')
-rw-r--r-- | static/plugins/auto-complete.js | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/static/plugins/auto-complete.js b/static/plugins/auto-complete.js new file mode 100644 index 0000000..d27f726 --- /dev/null +++ b/static/plugins/auto-complete.js @@ -0,0 +1,223 @@ +/* + JavaScript autoComplete v1.0.4 + Copyright (c) 2014 Simon Steinberger / Pixabay + GitHub: https://github.com/Pixabay/JavaScript-autoComplete + License: http://www.opensource.org/licenses/mit-license.php +*/ + +var autoComplete = (function(){ + + // "use strict"; + function autoComplete(options){ + if (!document.querySelector) return; + + // helpers + function hasClass(el, className){ return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className); } + + function addEvent(el, type, handler){ + if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); + } + function removeEvent(el, type, handler){ + // if (el.removeEventListener) not working in IE11 + if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); + } + function live(elClass, event, cb, context){ + addEvent(context || document, event, function(e){ + var found, el = e.target || e.srcElement; + while (el && !(found = hasClass(el, elClass))) el = el.parentElement; + if (found) cb.call(el, e); + }); + } + + var o = { + selector: 0, + source: 0, + minChars: 3, + delay: 150, + offsetLeft: 0, + offsetTop: 1, + cache: 1, + menuClass: '', + renderItem: function (item, search){ + // escape special characters + search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi"); + return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.replace(re, "<b>$1</b>") + '</div>'; + }, + onSelect: function(e, term, item){} + }; + for (var k in options) { if (options.hasOwnProperty(k)) o[k] = options[k]; } + + // init + var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector); + for (var i=0; i<elems.length; i++) { + var that = elems[i]; + + // create suggestions container "sc" + that.sc = document.createElement('div'); + that.sc.className = 'autocomplete-suggestions shadow '+o.menuClass; + + that.autocompleteAttr = that.getAttribute('autocomplete'); + that.setAttribute('autocomplete', 'off'); + that.cache = {}; + that.last_val = ''; + + that.updateSC = function(resize, next){ + var rect = that.getBoundingClientRect(); + that.sc.style.left = Math.round(rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + o.offsetLeft) + 'px'; + that.sc.style.top = Math.round(rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + o.offsetTop) + 'px'; + that.sc.style.width = Math.round(rect.right - rect.left) + 'px'; // outerWidth + if (!resize) { + that.sc.style.display = 'block'; + if (!that.sc.maxHeight) { that.sc.maxHeight = parseInt((window.getComputedStyle ? getComputedStyle(that.sc, null) : that.sc.currentStyle).maxHeight); } + if (!that.sc.suggestionHeight) that.sc.suggestionHeight = that.sc.querySelector('.autocomplete-suggestion').offsetHeight; + if (that.sc.suggestionHeight) + if (!next) that.sc.scrollTop = 0; + else { + var scrTop = that.sc.scrollTop, selTop = next.getBoundingClientRect().top - that.sc.getBoundingClientRect().top; + if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0) + that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight; + else if (selTop < 0) + that.sc.scrollTop = selTop + scrTop; + } + } + } + addEvent(window, 'resize', that.updateSC); + document.body.appendChild(that.sc); + + live('autocomplete-suggestion', 'mouseleave', function(e){ + var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); + if (sel) setTimeout(function(){ sel.className = sel.className.replace('selected', ''); }, 20); + }, that.sc); + + live('autocomplete-suggestion', 'mouseover', function(e){ + var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); + if (sel) sel.className = sel.className.replace('selected', ''); + this.className += ' selected'; + }, that.sc); + + live('autocomplete-suggestion', 'mousedown', function(e){ + if (hasClass(this, 'autocomplete-suggestion')) { // else outside click + var v = this.getAttribute('data-val'); + that.value = v; + o.onSelect(e, v, this); + that.sc.style.display = 'none'; + } + }, that.sc); + + that.blurHandler = function(){ + try { var over_sb = document.querySelector('.autocomplete-suggestions:hover'); } catch(e){ var over_sb = 0; } + if (!over_sb) { + that.last_val = that.value; + that.sc.style.display = 'none'; + setTimeout(function(){ that.sc.style.display = 'none'; }, 350); // hide suggestions on fast input + } else if (that !== document.activeElement) setTimeout(function(){ that.focus(); }, 20); + }; + addEvent(that, 'blur', that.blurHandler); + + var suggest = function(data){ + var val = that.value; + that.cache[val] = data; + if (data.length && val.length >= o.minChars) { + var s = ''; + for (var i=0;i<data.length;i++) s += o.renderItem(data[i], val); + that.sc.innerHTML = s; + that.updateSC(0); + } + else + that.sc.style.display = 'none'; + } + + that.keydownHandler = function(e){ + var key = window.event ? e.keyCode : e.which; + // down (40), up (38) + if ((key == 40 || key == 38) && that.sc.innerHTML) { + var next, sel = that.sc.querySelector('.autocomplete-suggestion.selected'); + if (!sel) { + next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last + next.className += ' selected'; + that.value = next.getAttribute('data-val'); + } else { + next = (key == 40) ? sel.nextSibling : sel.previousSibling; + if (next) { + sel.className = sel.className.replace('selected', ''); + next.className += ' selected'; + that.value = next.getAttribute('data-val'); + } + else { sel.className = sel.className.replace('selected', ''); that.value = that.last_val; next = 0; } + } + that.updateSC(0, next); + return false; + } + // esc + else if (key == 27) { that.value = that.last_val; that.sc.style.display = 'none'; } + // enter + else if (key == 13 || key == 9) { + var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); + if (sel && that.sc.style.display != 'none') { o.onSelect(e, sel.getAttribute('data-val'), sel); setTimeout(function(){ that.sc.style.display = 'none'; }, 20); } + } + }; + addEvent(that, 'keydown', that.keydownHandler); + + that.keyupHandler = function(e){ + var key = window.event ? e.keyCode : e.which; + if (!key || (key < 35 || key > 40) && key != 13 && key != 27) { + var val = that.value; + if (val.length >= o.minChars) { + if (val != that.last_val) { + that.last_val = val; + clearTimeout(that.timer); + if (o.cache) { + if (val in that.cache) { suggest(that.cache[val]); return; } + // no requests if previous suggestions were empty + for (var i=1; i<val.length-o.minChars; i++) { + var part = val.slice(0, val.length-i); + if (part in that.cache && !that.cache[part].length) { suggest([]); return; } + } + } + that.timer = setTimeout(function(){ o.source(val, suggest) }, o.delay); + } + } else { + that.last_val = val; + that.sc.style.display = 'none'; + } + } + }; + addEvent(that, 'keyup', that.keyupHandler); + + that.focusHandler = function(e){ + that.last_val = '\n'; + that.keyupHandler(e) + }; + if (!o.minChars) addEvent(that, 'focus', that.focusHandler); + } + + // public destroy method + this.destroy = function(){ + for (var i=0; i<elems.length; i++) { + var that = elems[i]; + removeEvent(window, 'resize', that.updateSC); + removeEvent(that, 'blur', that.blurHandler); + removeEvent(that, 'focus', that.focusHandler); + removeEvent(that, 'keydown', that.keydownHandler); + removeEvent(that, 'keyup', that.keyupHandler); + if (that.autocompleteAttr) + that.setAttribute('autocomplete', that.autocompleteAttr); + else + that.removeAttribute('autocomplete'); + document.body.removeChild(that.sc); + that = null; + } + }; + } + return autoComplete; +})(); + +(function(){ + if (typeof define === 'function' && define.amd) + define('autoComplete', function () { return autoComplete; }); + else if (typeof module !== 'undefined' && module.exports) + module.exports = autoComplete; + else + window.autoComplete = autoComplete; +})(); |