vendor: 10,370 bytes, lines 1-411
1/** 2 * Simple, lightweight, usable local autocomplete library for modern browsers 3 * Because there werenât enough autocomplete scripts in the world? Because Iâm completely insane and have NIH syndrome? Probably both. :P 4 * @author Lea Verou http://leaverou.github.io/awesomplete 5 * MIT license 6 */ 7 8(function () { 9 10var _ = function (input, o) { 11 var me = this; 12 13 // Keep track of number of instances for unique IDs 14 _.count = (_.count || 0) + 1; 15 this.count = _.count; 16 17 // Setup 18 19 this.isOpened = false; 20 21 this.input = $(input); 22 this.input.setAttribute("autocomplete", "off"); 23 this.input.setAttribute("aria-expanded", "false"); 24 this.input.setAttribute("aria-owns", "awesomplete_list_" + this.count); 25 this.input.setAttribute("role", "combobox"); 26 27 // store constructor options in case we need to distinguish 28 // between default and customized behavior later on 29 this.options = o = o || {}; 30 31 configure(this, { 32 minChars: 2, 33 maxItems: 10, 34 autoFirst: false, 35 data: _.DATA, 36 filter: _.FILTER_CONTAINS, 37 sort: o.sort === false ? false : _.SORT_BYLENGTH, 38 container: _.CONTAINER, 39 item: _.ITEM, 40 replace: _.REPLACE, 41 tabSelect: false, 42 listLabel: "Results List" 43 }, o); 44 45 this.index = -1; 46 47 // Create necessary elements 48 49 this.container = this.container(input); 50 51 this.ul = $.create("ul", { 52 hidden: "hidden", 53 role: "listbox", 54 id: "awesomplete_list_" + this.count, 55 inside: this.container, 56 "aria-label": this.listLabel 57 }); 58 59 this.status = $.create("span", { 60 className: "visually-hidden", 61 role: "status", 62 "aria-live": "assertive", 63 "aria-atomic": true, 64 inside: this.container, 65 textContent: this.minChars != 0 ? ("Type " + this.minChars + " or more characters for results.") : "Begin typing for results." 66 }); 67 68 // Bind events 69 70 this._events = { 71 input: { 72 "input": this.evaluate.bind(this), 73 "blur": this.close.bind(this, { reason: "blur" }), 74 "keydown": function(evt) { 75 var c = evt.keyCode; 76 77 // If the dropdown `ul` is in view, then act on keydown for the following keys: 78 // Enter / Esc / Up / Down 79 if(me.opened) { 80 if (c === 13 && me.selected) { // Enter 81 evt.preventDefault(); 82 me.select(undefined, undefined, evt); 83 } 84 else if (c === 9 && me.selected && me.tabSelect) { 85 me.select(undefined, undefined, evt); 86 } 87 else if (c === 27) { // Esc 88 me.close({ reason: "esc" }); 89 } 90 else if (c === 38 || c === 40) { // Down/Up arrow 91 evt.preventDefault(); 92 me[c === 38? "previous" : "next"](); 93 } 94 } 95 } 96 }, 97 form: { 98 "submit": this.close.bind(this, { reason: "submit" }) 99 }, 100 ul: { 101 // Prevent the default mousedowm, which ensures the input is not blurred. 102 // The actual selection will happen on click. This also ensures dragging the 103 // cursor away from the list item will cancel the selection 104 "mousedown": function(evt) { 105 evt.preventDefault(); 106 }, 107 // The click event is fired even if the corresponding mousedown event has called preventDefault 108 "click": function(evt) { 109 var li = evt.target; 110 111 if (li !== this) { 112 113 while (li && !/li/i.test(li.nodeName)) { 114 li = li.parentNode; 115 } 116 117 if (li && evt.button === 0) { // Only select on left click 118 evt.preventDefault(); 119 me.select(li, evt.target, evt); 120 } 121 } 122 } 123 } 124 }; 125 126 $.bind(this.input, this._events.input); 127 $.bind(this.input.form, this._events.form); 128 $.bind(this.ul, this._events.ul); 129 130 if (this.input.hasAttribute("list")) { 131 this.list = "#" + this.input.getAttribute("list"); 132 this.input.removeAttribute("list"); 133 } 134 else { 135 this.list = this.input.getAttribute("data-list") || o.list || []; 136 } 137 138 _.all.push(this); 139}; 140 141_.prototype = { 142 set list(list) { 143 if (Array.isArray(list)) { 144 this._list = list; 145 } 146 else if (typeof list === "string" && list.indexOf(",") > -1) { 147 this._list = list.split(/\s*,\s*/); 148 } 149 else { // Element or CSS selector 150 list = $(list); 151 152 if (list && list.children) { 153 var items = []; 154 slice.apply(list.children).forEach(function (el) { 155 if (!el.disabled) { 156 var text = el.textContent.trim(); 157 var value = el.value || text; 158 var label = el.label || text; 159 if (value !== "") { 160 items.push({ label: label, value: value }); 161 } 162 } 163 }); 164 this._list = items; 165 } 166 } 167 168 if (document.activeElement === this.input) { 169 this.evaluate(); 170 } 171 }, 172 173 get selected() { 174 return this.index > -1; 175 }, 176 177 get opened() { 178 return this.isOpened; 179 }, 180 181 close: function (o) { 182 if (!this.opened) { 183 return; 184 } 185 186 this.input.setAttribute("aria-expanded", "false"); 187 this.ul.setAttribute("hidden", ""); 188 this.isOpened = false; 189 this.index = -1; 190 191 this.status.setAttribute("hidden", ""); 192 193 $.fire(this.input, "awesomplete-close", o || {}); 194 }, 195 196 open: function () { 197 this.input.setAttribute("aria-expanded", "true"); 198 this.ul.removeAttribute("hidden"); 199 this.isOpened = true; 200 201 this.status.removeAttribute("hidden"); 202 203 if (this.autoFirst && this.index === -1) { 204 this.goto(0); 205 } 206 207 $.fire(this.input, "awesomplete-open"); 208 }, 209 210 destroy: function() { 211 //remove events from the input and its form 212 $.unbind(this.input, this._events.input); 213 $.unbind(this.input.form, this._events.form); 214 215 // cleanup container if it was created by Awesomplete but leave it alone otherwise 216 if (!this.options.container) { 217 //move the input out of the awesomplete container and remove the container and its children 218 var parentNode = this.container.parentNode; 219 220 parentNode.insertBefore(this.input, this.container); 221 parentNode.removeChild(this.container); 222 } 223 224 //remove autocomplete and aria-autocomplete attributes 225 this.input.removeAttribute("autocomplete"); 226 this.input.removeAttribute("aria-autocomplete"); 227 228 //remove this awesomeplete instance from the global array of instances 229 var indexOfAwesomplete = _.all.indexOf(this); 230 231 if (indexOfAwesomplete !== -1) { 232 _.all.splice(indexOfAwesomplete, 1); 233 } 234 }, 235 236 next: function () { 237 var count = this.ul.children.length; 238 this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) ); 239 }, 240 241 previous: function () { 242 var count = this.ul.children.length; 243 var pos = this.index - 1; 244 245 this.goto(this.selected && pos !== -1 ? pos : count - 1); 246 }, 247 248 // Should not be used, highlights specific item without any checks! 249 goto: function (i) { 250 var lis = this.ul.children; 251 252 if (this.selected) { 253 lis[this.index].setAttribute("aria-selected", "false"); 254 } 255 256 this.index = i; 257 258 if (i > -1 && lis.length > 0) { 259 lis[i].setAttribute("aria-selected", "true"); 260 261 this.status.textContent = lis[i].textContent + ", list item " + (i + 1) + " of " + lis.length; 262 263 this.input.setAttribute("aria-activedescendant", this.ul.id + "_item_" + this.index); 264 265 // scroll to highlighted element in case parent's height is fixed 266 this.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight; 267 268 $.fire(this.input, "awesomplete-highlight", { 269 text: this.suggestions[this.index] 270 }); 271 } 272 }, 273 274 select: function (selected, origin, originalEvent) { 275 if (selected) { 276 this.index = $.siblingIndex(selected); 277 } else { 278 selected = this.ul.children[this.index]; 279 } 280 281 if (selected) { 282 var suggestion = this.suggestions[this.index]; 283 284 var allowed = $.fire(this.input, "awesomplete-select", { 285 text: suggestion, 286 origin: origin || selected, 287 originalEvent: originalEvent 288 }); 289 290 if (allowed) { 291 this.replace(suggestion); 292 this.close({ reason: "select" }); 293 $.fire(this.input, "awesomplete-selectcomplete", { 294 text: suggestion, 295 originalEvent: originalEvent 296 }); 297 } 298 } 299 }, 300 301 evaluate: function() { 302 var me = this; 303 var value = this.input.value; 304 305 if (value.length >= this.minChars && this._list && this._list.length > 0) { 306 this.index = -1; 307 // Populate list with options that match 308 this.ul.innerHTML = ""; 309 310 this.suggestions = this._list 311 .map(function(item) { 312 return new Suggestion(me.data(item, value)); 313 }) 314 .filter(function(item) { 315 return me.filter(item, value); 316 }); 317 318 if (this.sort !== false) { 319 this.suggestions = this.suggestions.sort(this.sort); 320 } 321 322 this.suggestions = this.suggestions.slice(0, this.maxItems); 323 324 this.suggestions.forEach(function(text, index) { 325 me.ul.appendChild(me.item(text, value, index)); 326 }); 327 328 if (this.ul.children.length === 0) { 329 330 this.status.textContent = "No results found"; 331 332 this.close({ reason: "nomatches" }); 333 334 } else { 335 this.open(); 336 337 this.status.textContent = this.ul.children.length + " results found"; 338 } 339 } 340 else { 341 this.close({ reason: "nomatches" }); 342 343 this.status.textContent = "No results found"; 344 } 345 } 346}; 347 348// Static methods/properties 349 350_.all = []; 351 352_.FILTER_CONTAINS = function (text, input) { 353 return RegExp($.regExpEscape(input.trim()), "i").test(text); 354}; 355 356_.FILTER_STARTSWITH = function (text, input) { 357 return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text); 358}; 359 360_.SORT_BYLENGTH = function (a, b) { 361 if (a.length !== b.length) { 362 return a.length - b.length; 363 } 364 365 return a < b? -1 : 1; 366}; 367 368_.CONTAINER = function (input) { 369 return $.create("div", { 370 className: "awesomplete", 371 around: input 372 }); 373} 374 375_.ITEM = function (text, input, item_id) { 376 var html = input.trim() === "" ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "<mark>$&</mark>"); 377 return $.create("li", { 378 innerHTML: html, 379 "role": "option", 380 "aria-selected": "false", 381 "id": "awesomplete_list_" + this.count + "_item_" + item_id 382 }); 383}; 384 385_.REPLACE = function (text) { 386 this.input.value = text.value; 387}; 388 389_.DATA = function (item/*, input*/) { return item; }; 390 391// Private functions 392 393function Suggestion(data) { 394 var o = Array.isArray(data) 395 ? { label: data[0], value: data[1] } 396 : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data }; 397 398 this.label = o.label || o.value; 399 this.value = o.value; 400} 401Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", { 402 get: function() { return this.label.length; } 403}); 404Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () { 405 return "" + this.label; 406}; 407 408function configure(instance, properties, o) { 409 for (var i in properties) { 410 var initial = properties[i], 411
vendor: 2,766 bytes, lines 411-554
411attrValue = instance.input.getAttribute("data-" + i.toLowerCase()); 412 413 if (typeof initial === "number") { 414 instance[i] = parseInt(attrValue); 415 } 416 else if (initial === false) { // Boolean options must be false by default anyway 417 instance[i] = attrValue !== null; 418 } 419 else if (initial instanceof Function) { 420 instance[i] = null; 421 } 422 else { 423 instance[i] = attrValue; 424 } 425 426 if (!instance[i] && instance[i] !== 0) { 427 instance[i] = (i in o)? o[i] : initial; 428 } 429 } 430} 431 432// Helpers 433 434var slice = Array.prototype.slice; 435 436function $(expr, con) { 437 return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; 438} 439 440function $$(expr, con) { 441 return slice.call((con || document).querySelectorAll(expr)); 442} 443 444$.create = function(tag, o) { 445 var element = document.createElement(tag); 446 447 for (var i in o) { 448 var val = o[i]; 449 450 if (i === "inside") { 451 $(val).appendChild(element); 452 } 453 else if (i === "around") { 454 var ref = $(val); 455 ref.parentNode.insertBefore(element, ref); 456 element.appendChild(ref); 457 458 if (ref.getAttribute("autofocus") != null) { 459 ref.focus(); 460 } 461 } 462 else if (i in element) { 463 element[i] = val; 464 } 465 else { 466 element.setAttribute(i, val); 467 } 468 } 469 470 return element; 471}; 472 473$.bind = function(element, o) { 474 if (element) { 475 for (var event in o) { 476 var callback = o[event]; 477 478 event.split(/\s+/).forEach(function (event) { 479 element.addEventListener(event, callback); 480 }); 481 } 482 } 483}; 484 485$.unbind = function(element, o) { 486 if (element) { 487 for (var event in o) { 488 var callback = o[event]; 489 490 event.split(/\s+/).forEach(function(event) { 491 element.removeEventListener(event, callback); 492 }); 493 } 494 } 495}; 496 497$.fire = function(target, type, properties) { 498 var evt = document.createEvent("HTMLEvents"); 499 500 evt.initEvent(type, true, true ); 501 502 for (var j in properties) { 503 evt[j] = properties[j]; 504 } 505 506 return target.dispatchEvent(evt); 507}; 508 509$.regExpEscape = function (s) { 510 return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); 511}; 512 513$.siblingIndex = function (el) { 514 /* eslint-disable no-cond-assign */ 515 for (var i = 0; el = el.previousElementSibling; i++); 516 return i; 517}; 518 519// Initialization 520 521function init() { 522 $$("input.awesomplete").forEach(function (input) { 523 new _(input); 524 }); 525} 526 527// Make sure to export Awesomplete on self when in a browser 528if (typeof self !== "undefined") { 529 self.Awesomplete = _; 530} 531 532// Are we in a browser? Check for Document constructor 533if (typeof Document !== "undefined") { 534 // DOM already loaded? 535 if (document.readyState !== "loading") { 536 init(); 537 } 538 else { 539 // Wait for it 540 document.addEventListener("DOMContentLoaded", init); 541 } 542} 543 544_.$ = $; 545_.$$ = $$; 546 547// Expose Awesomplete as a CJS module 548if (typeof module === "object" && module.exports) { 549 module.exports = _; 550} 551 552return _; 553 554}());
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.