1/* 2 JavaScript autoComplete v1.0.4 3 Copyright (c) 2014 Simon Steinberger / Pixabay 4 GitHub: https://github.com/Pixabay/JavaScript-autoComplete 5 License: http://www.opensource.org/licenses/mit-license.php 6*/ 7 8var autoComplete = (function(){ 9 // "use strict"; 10 function autoComplete(options){ 11 if (!document.querySelector) return; 12 13 // helpers 14 function hasClass(el, className){ return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className); } 15 16 function addEvent(el, type, handler){ 17 if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); 18 } 19 function removeEvent(el, type, handler){ 20 // if (el.removeEventListener) not working in IE11 21 if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); 22 } 23 function live(elClass, event, cb, context){ 24 addEvent(context || document, event, function(e){ 25 var found, el = e.target || e.srcElement; 26 while (el && !(found = hasClass(el, elClass))) el = el.parentElement; 27 if (found) cb.call(el, e); 28 }); 29 } 30 31 var o = { 32 selector: 0, 33 source: 0, 34 minChars: 3, 35 delay: 150, 36 offsetLeft: 0, 37 offsetTop: 1, 38 cache: 1, 39 menuClass: '', 40 renderItem: function (item, search){ 41 // escape special characters 42 search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 43 var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi"); 44 return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.replace(re, "<b>$1</b>") + '</div>'; 45 }, 46 onSelect: function(e, term, item){} 47 }; 48 for (var k in options) { if (options.hasOwnProperty(k)) o[k] = options[k]; } 49 50 // init 51 var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector); 52 for (var i=0; i<elems.length; i++) { 53 var that = elems[i]; 54 55 // create suggestions container "sc" 56 that.sc = document.createElement('div'); 57 that.sc.className = 'autocomplete-suggestions '+o.menuClass; 58 59 that.autocompleteAttr = that.getAttribute('autocomplete'); 60 that.setAttribute('autocomplete', 'off'); 61 that.cache = {}; 62 that.last_val = ''; 63 64 that.updateSC = function(resize, next){ 65 var rect = that.getBoundingClientRect(); 66 that.sc.style.left = Math.round(rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + o.offsetLeft) + 'px'; 67 that.sc.style.top = Math.round(rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + o.offsetTop) + 'px'; 68 that.sc.style.width = Math.round(rect.right - rect.left) + 'px'; // outerWidth 69 if (!resize) { 70 that.sc.style.display = 'block'; 71 if (!that.sc.maxHeight) { that.sc.maxHeight = parseInt((window.getComputedStyle ? getComputedStyle(that.sc, null) : that.sc.currentStyle).maxHeight); } 72 if (!that.sc.suggestionHeight) that.sc.suggestionHeight = that.sc.querySelector('.autocomplete-suggestion').offsetHeight; 73 if (that.sc.suggestionHeight) 74 if (!next) that.sc.scrollTop = 0; 75 else { 76 var scrTop = that.sc.scrollTop, selTop = next.getBoundingClientRect().top - that.sc.getBoundingClientRect().top; 77 if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0) 78 that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight; 79 else if (selTop < 0) 80 that.sc.scrollTop = selTop + scrTop; 81 } 82 } 83 } 84 addEvent(window, 'resize', that.updateSC); 85 document.body.appendChild(that.sc); 86 87 live('autocomplete-suggestion', 'mouseleave', function(e){ 88 var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); 89 if (sel) setTimeout(function(){ sel.className = sel.className.replace('selected', ''); }, 20); 90 }, that.sc); 91 92 live('autocomplete-suggestion', 'mouseover', function(e){ 93 var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); 94 if (sel) sel.className = sel.className.replace('selected', ''); 95 this.className += ' selected'; 96 }, that.sc); 97 98 live('autocomplete-suggestion', 'mousedown', function(e){ 99 if (hasClass(this, 'autocomplete-suggestion')) { // else outside click 100 var v = this.getAttribute('data-val'); 101 that.value = v; 102 o.onSelect(e, v, this); 103 that.sc.style.display = 'none'; 104 } 105 }, that.sc); 106 107 that.blurHandler = function(){ 108 try { var over_sb = document.querySelector('.autocomplete-suggestions:hover'); } catch(e){ var over_sb = 0; } 109 if (!over_sb) { 110 that.last_val = that.value; 111 that.sc.style.display = 'none'; 112 setTimeout(function(){ that.sc.style.display = 'none'; }, 350); // hide suggestions on fast input 113 } else if (that !== document.activeElement) setTimeout(function(){ that.focus(); }, 20); 114 }; 115 addEvent(that, 'blur', that.blurHandler); 116 117 var suggest = function(data){ 118 var val = that.value; 119 that.cache[val] = data; 120 if (data.length && val.length >= o.minChars) { 121 var s = ''; 122 for (var i=0;i<data.length;i++) s += o.renderItem(data[i], val); 123 that.sc.innerHTML = s; 124 that.updateSC(0); 125 } 126 else 127 that.sc.style.display = 'none'; 128 } 129 130 that.keydownHandler = function(e){ 131 var key = window.event ? e.keyCode : e.which; 132 // down (40), up (38) 133 if ((key == 40 || key == 38) && that.sc.innerHTML) { 134 var next, sel = that.sc.querySelector('.autocomplete-suggestion.selected'); 135 if (!sel) { 136 next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last 137 next.className += ' selected'; 138 that.value = next.getAttribute('data-val'); 139 } else { 140 next = (key == 40) ? sel.nextSibling : sel.previousSibling; 141 if (next) { 142 sel.className = sel.className.replace('selected', ''); 143 next.className += ' selected'; 144 that.value = next.getAttribute('data-val'); 145 } 146 else { sel.className = sel.className.replace('selected', ''); that.value = that.last_val; next = 0; } 147 } 148 that.updateSC(0, next); 149 return false;
150 } 151 // esc 152 else if (key == 27) { that.value = that.last_val; that.sc.style.display = 'none'; } 153 // enter 154 else if (key == 13 || key == 9) { 155 var sel = that.sc.querySelector('.autocomplete-suggestion.selected'); 156 if (sel && that.sc.style.display != 'none') { o.onSelect(e, sel.getAttribute('data-val'), sel); setTimeout(function(){ that.sc.style.display = 'none'; }, 20); } 157 } 158 }; 159 addEvent(that, 'keydown', that.keydownHandler); 160 161 that.keyupHandler = function(e){ 162 var key = window.event ? e.keyCode : e.which; 163 if (!key || (key < 35 || key > 40) && key != 13 && key != 27) { 164 var val = that.value; 165 if (val.length >= o.minChars) { 166 if (val != that.last_val) { 167 that.last_val = val; 168 clearTimeout(that.timer); 169 if (o.cache) { 170 if (val in that.cache) { suggest(that.cache[val]); return; } 171 // no requests if previous suggestions were empty 172 for (var i=1; i<val.length-o.minChars; i++) { 173 var part = val.slice(0, val.length-i); 174 if (part in that.cache && !that.cache[part].length) { suggest([]); return; } 175 } 176 } 177 that.timer = setTimeout(function(){ o.source(val, suggest) }, o.delay); 178 } 179 } else { 180 that.last_val = val; 181 that.sc.style.display = 'none'; 182 } 183 } 184 }; 185 addEvent(that, 'keyup', that.keyupHandler); 186 187 that.focusHandler = function(e){ 188 that.last_val = '\n'; 189 that.keyupHandler(e) 190 }; 191 if (!o.minChars) addEvent(that, 'focus', that.focusHandler); 192 } 193 194 // public destroy method 195 this.destroy = function(){ 196 for (var i=0; i<elems.length; i++) { 197 var that = elems[i]; 198 removeEvent(window, 'resize', that.updateSC); 199 removeEvent(that, 'blur', that.blurHandler); 200 removeEvent(that, 'focus', that.focusHandler); 201 removeEvent(that, 'keydown', that.keydownHandler); 202 removeEvent(that, 'keyup', that.keyupHandler); 203 if (that.autocompleteAttr) 204 that.setAttribute('autocomplete', that.autocompleteAttr); 205 else 206 that.removeAttribute('autocomplete'); 207 document.body.removeChild(that.sc); 208 that = null; 209 } 210 }; 211 } 212 return autoComplete; 213})(); 214 215(function(){ 216 if (typeof define === 'function' && define.amd) 217 define('autoComplete', function () { return autoComplete; }); 218 else if (typeof module !== 'undefined' && module.exports) 219 module.exports = autoComplete; 220 else 221 window.autoComplete = autoComplete; 222})();
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.