UNPKG

iuap-design

Version:
571 lines (544 loc) 15.9 kB
u.Autocomplete = u.BaseComponent.extend({ defaults: { inputClass: "ac_input", resultsClass: "ac_results", lineSeparator: "\n", cellSeparator: "|", minChars: 1, delay: 400, matchCase: 0, matchSubset: 1, matchContains: 0, cacheLength: 1, mustMatch: 0, extraParams: {}, loadingClass: "ac_loading", selectFirst: false, selectOnly: false, maxItemsToShow: -1, autoFill: false, width: 0, source:null, select: null, multiSelect: false, //moreClick:function(){}, }, init: function(){ var self = this; this.options = u.extend({}, this.defaults, this.options); this.requestIndex = 0; this.pending = 0; if (this.options.inputClass){ u.addClass(this.element, this.options.inputClass); } this._results = document.querySelector('#autocompdiv'); if (!this._results){ this._results = u.makeDOM('<div id="autocompdiv"></div>'); document.body.appendChild(this._results); } this._results.style.display = 'none'; this._results.style.position = 'absolute'; u.addClass(this._results, this.options.resultsClass); if (this.options.width){ this._results.style.width = this.options.width; } this.timeout = null; this.prev = ""; this.active = -1; this.cache = {}; this.keyb = false; this.hasFocus = false; this.lastKeyPressCode = null; this._initSource(); u.on(this.element,'keydown', function(e){ self.lastKeyPressCode = e.keyCode; switch (e.keyCode) { case 38: // up u.stopEvent(e); self.moveSelect(-1); break; case 40: // down u.stopEvent(e); self.moveSelect(1); break; case 9: // tab case 13: // return if (self.selectCurrent()) { // make sure to blur off the current field // self.element.blur(); u.stopEvent(e); } break; default: self.active = -1; if (self.timeout) clearTimeout(self.timeout); self.timeout = setTimeout(function() { self.onChange(); }, self.options.delay); break; } }); u.on(this.element,'focus', function(){ self.hasFocus = true; }); u.on(this.element,'blur', function(){ self.hasFocus = false; self.hideResults(); }); this.hideResultsNow(); }, flushCache: function(){ this.cache = {}; this.cache.data = {}; this.cache.length = 0; }, _initSource: function(){ var array, url, self = this; if ( u.isArray( this.options.source ) ) { array = this.options.source; this.source = function( request, response ) { // response( $.ui.autocomplete.filter( array, request.term ) ); response(self.filterData(request.term, array)); }; } else if ( typeof this.options.source === "string" ) { url = this.options.source; this.source = function( request, response ) { if (self.xhr) { self.xhr.abort(); } self.xhr = u.ajax({ url: url, data: request, dataType: "json", success: function( data ) { response( data ); }, error: function() { response([]); } }); }; } else { this.source = this.options.source; } }, _response: function(){ var self = this; var index = ++this.requestIndex; return function( content ) { if ( index === self.requestIndex ) { self.__response( content ); } self.pending--; if ( !self.pending ) { } }; }, __response: function(content){ if ( content ) this.receiveData2(content); this.showResults(); }, onChange: function(){ // ignore if the following keys are pressed: [del] [shift] [capslock] if (this.lastKeyPressCode == 46 || (this.lastKeyPressCode > 8 && this.lastKeyPressCode < 32)) return this._results.style.disply='none'; if(!this.element.value) return; var vs = this.element.value.split(','), v = vs[vs.length-1].trim() if (v == this.prev) return; this.prev = v; if (v.length >= this.options.minChars) { u.addClass(this.element,this.options.loadingClass); this.pending++; this.source( { term: v }, this._response() ); } else { u.removeClass(this.element, this.options.loadingClass); this._results.style.display='none'; } }, moveSelect: function(step){ var lis = this._results.querySelectorAll('li'); if (!lis) return; this.active += step; if (this.active < 0) { this.active = 0; } else if (this.active >= lis.length) { this.active = lis.length - 1; } lis.forEach(function(li){ u.removeClass(li, 'ac_over'); }); u.addClass(lis[this.active], 'ac_over'); }, selectCurrent: function () { var li = this._results.querySelector('li.ac_over'); //$("li.ac_over", this.$results[0])[0]; if (!li) { var _li = this._results.querySelectorAll('li'); //$("li", this.$results[0]); if (this.options.selectOnly) { if (_li.length == 1) li = _li[0]; } else if (this.options.selectFirst) { li = _li[0]; } } if (li) { this.selectItem(li); return true; } else { return false; } }, selectItem: function(li){ var self = this; if (!li) { li = document.createElement("li"); li.selectValue = ""; } var v = li.selectValue ? li.selectValue : li.innerHTML; this.lastSelected = v; this.prev = v; this._results.innerHTML = ''; if(this.options.multiSelect) { if ((this.element.value + ',').indexOf(v + ',') != -1) return; var vs = this.element.value.split(','); var lastValue = this.element.value.substring(0, this.element.value.lastIndexOf(',')); this.element.value = (lastValue ? lastValue + ', ' : lastValue) + v + ', '; } else { this.element.value = v; } this.hideResultsNow(); this.element.focus(); if (this.options.select) setTimeout(function() { self.options.select(li._item, self) }, 1); }, createSelection: function(start, end){ // get a reference to the input element var field = this.element; if (field.createTextRange) { var selRange = field.createTextRange(); selRange.collapse(true); selRange.moveStart("character", start); selRange.moveEnd("character", end); selRange.select(); } else if (field.setSelectionRange) { field.setSelectionRange(start, end); } else { if (field.selectionStart) { field.selectionStart = start; field.selectionEnd = end; } } field.focus(); }, // fills in the input box w/the first match (assumed to be the best match) autoFill: function(sValue){ // if the last user key pressed was backspace, don't autofill if (this.lastKeyPressCode != 8) { // fill in the value (keep the case the user has typed) this.element.value = this.element.value + sValue.substring(this.prev.length); // select the portion of the value not typed by the user (so the next character will erase) this.createSelection(this.prev.length, sValue.length); } }, showResults: function(){ // get the position of the input field right now (in case the DOM is shifted) var pos = findPos(this.element); // either use the specified width, or autocalculate based on form element var iWidth = (this.options.width > 0) ? this.options.width : this.element.offsetWidth; // reposition if('100%'===this.options.width){ this._results.style.top = (pos.y + this.element.offsetHeight) + "px"; this._results.style.left = pos.x + "px"; this._results.style.display = 'block'; }else{ this._results.style.width = parseInt(iWidth) + "px"; this._results.style.top = (pos.y + this.element.offsetHeight) + "px"; this._results.style.left = pos.x + "px"; this._results.style.display = 'block'; } }, hideResults: function(){ var self = this; if (this.timeout) clearTimeout(this.timeout); this.timeout = setTimeout(function() { self.hideResultsNow(); }, 200); }, hideResultsNow: function(){ if (this.timeout) clearTimeout(this.timeout); u.removeClass(this.element, this.options.loadingClass); //if (this.$results.is(":visible")) { this._results.style.display = 'none'; //} if (this.options.mustMatch) { var v = this.element.value; if (v != this.lastSelected) { this.selectItem(null); } } }, receiveData:function(q, data){ if (data) { u.removeClass(this.element,this.options.loadingClass); this._results.innerHTML = ''; if (!this.hasFocus || data.length == 0) return this.hideResultsNow(); this._results.appendChild(this.dataToDom(data)); // autofill in the complete box w/the first match as long as the user hasn't entered in more data if (this.options.autoFill && (this.element.value.toLowerCase() == q.toLowerCase())) this.autoFill(data[0][0]); this.showResults(); } else { this.hideResultsNow(); } }, filterData: function(v, items){ if (!v) return items; var _items = []; for (var i =0, count = items.length; i< count; i++){ var label = items[i].label; if (label.indexOf(v) > -1) _items.push(items[i]); } return _items; }, receiveData2: function(items){ if (items) { u.removeClass(this.element, this.options.loadingClass); this._results.innerHTML = ''; // if the field no longer has focus or if there are no matches, do not display the drop down if (!this.hasFocus || items.length == 0) return this.hideResultsNow(); this._results.appendChild(this.dataToDom2(items)); this.showResults(); } else { this.hideResultsNow(); } }, dataToDom2: function(items){ var ul = document.createElement("ul"); var num = items.length; var me = this; var showMoreMenu = false; // limited results to a max number if ((this.options.maxItemsToShow > 0) && (this.options.maxItemsToShow < num)){ num = this.options.maxItemsToShow; if(this.options.moreMenuClick){ showMoreMenu = true; } } for (var i = 0; i < num; i++) { var item = items[i]; if (!item) continue; var li = document.createElement("li"); if (this.options.formatItem) li.innerHTML = this.options.formatItem(item, i, num); else li.innerHTML = item.label; li.selectValue = item.label; li._item = item; ul.appendChild(li); u.on(li, 'mouseenter', function(){ var _li = ul.querySelector('li.ac_over'); if (_li) u.removeClass(_li, 'ac_over');; u.addClass(this,"ac_over"); me.active = indexOf(ul.querySelectorAll('li'), this); }); u.on(li,'mouseleave', function(){ u.removeClass(this, "ac_over"); }); u.on(li, 'mousedown', function(e){ u.stopEvent(e); me.selectItem(this); }); } if(showMoreMenu){ var li = document.createElement("li"); li.innerHTML = '更多'; ul.appendChild(li); u.on(li, 'mouseenter', function(){ var _li = ul.querySelector('li.ac_over'); if (_li) u.removeClass(_li, 'ac_over');; u.addClass(this,"ac_over"); }); u.on(li,'mouseleave', function(){ u.removeClass(this, "ac_over"); }); u.on(li, 'mousedown', function(e){ u.stopEvent(e); me.options.moreMenuClick.call(me); }); } return ul; }, parseData: function(){ if (!data) return null; var parsed = []; var rows = data.split(this.options.lineSeparator); for (var i = 0; i < rows.length; i++) { var row = rows[i]; if (row) { parsed[parsed.length] = row.split(this.options.cellSeparator); } } return parsed; }, dataToDom: function(data){ var ul = document.createElement("ul"); var num = data.length; var self = this; var showMoreMenu = false; // limited results to a max number if ((this.options.maxItemsToShow > 0) && (this.options.maxItemsToShow < num)){ num = this.options.maxItemsToShow; if(this.options.moreMenuClick){ showMoreMenu = true; } } for (var i = 0; i < num; i++) { var row = data[i]; if (!row) continue; var li = document.createElement("li"); if (this.options.formatItem) { li.innerHTML = this.options.formatItem(row, i, num); li.selectValue = row[0]; } else { li.innerHTML = row[0]; li.selectValue = row[0]; } var extra = null; if (row.length > 1) { extra = []; for (var j = 1; j < row.length; j++) { extra[extra.length] = row[j]; } } li.extra = extra; ul.appendChild(li); u.on(li, 'mouseenter', function(){ var _li = ul.querySelector('li.ac_over'); if (_li) u.removeClass(_li, 'ac_over');; u.addClass(this,"ac_over"); self.active = indexOf(ul.querySelectorAll('li'), this); }); u.on(li,'mouseleave', function(){ u.removeClass(this, "ac_over"); }); u.on(li, 'mousedown', function(){ u.stopEvent(e); self.selectItem(this); }); } if(showMoreMenu){ var li = document.createElement("li"); li.innerHTML = '更多'; ul.appendChild(li); u.on(li, 'mouseenter', function(){ var _li = ul.querySelector('li.ac_over'); if (_li) u.removeClass(_li, 'ac_over');; u.addClass(this,"ac_over"); }); u.on(li,'mouseleave', function(){ u.removeClass(this, "ac_over"); }); u.on(li, 'mousedown', function(e){ u.stopEvent(e); self.options.moreMenuClick.call(self); }); } return ul; }, requestData: function(){ var self = this; if (!this.options.matchCase) q = q.toLowerCase(); var data = this.options.cacheLength ? this.loadFromCache(q) : null; // recieve the cached data if (data) { this.receiveData(q, data); // if an AJAX url has been supplied, try loading the data now } else if ((typeof this.options.url == "string") && (this.options.url.length > 0)) { u.ajax({ url:this.makeUrl(q), success: function(data){ data = self.parseData(data); self.addToCache(q, data); self.receiveData(q, data); } }) // if there's been no data found, remove the loading class } else { u.removeClass(this.element,this.options.loadingClass); } }, makeUrl: function(q){ var url = this.options.url + "?q=" + encodeURI(q); for (var i in this.options.extraParams) { url += "&" + i + "=" + encodeURI(this.options.extraParams[i]); } return url; }, loadFromCache: function(){ if (!q) return null; if (this.cache.data[q]) return this.cache.data[q]; if (this.options.matchSubset) { for (var i = q.length - 1; i >= this.options.minChars; i--) { var qs = q.substr(0, i); var c = this.cache.data[qs]; if (c) { var csub = []; for (var j = 0; j < c.length; j++) { var x = c[j]; var x0 = x[0]; if (this.matchSubset(x0, q)) { csub[csub.length] = x; } } return csub; } } } return null; }, matchSubset: function(s, sub){ if (!this.options.matchCase) s = s.toLowerCase(); var i = s.indexOf(sub); if (i == -1) return false; return i == 0 || this.options.matchContains; }, addToCache: function(q, data){ if (!data || !q || !this.options.cacheLength) return; if (!this.cache.length || this.cache.length > this.options.cacheLength) { this.flushCache(); this.cache.length++; } else if (!this.cache[q]) { this.cache.length++; } this.cache.data[q] = data; } }); function findPos(obj) { var curleft = obj.offsetLeft || 0; var curtop = obj.offsetTop || 0; while (obj = obj.offsetParent) { curleft += obj.offsetLeft curtop += obj.offsetTop } return { x: curleft, y: curtop }; } function indexOf(element, e) { for (var i = 0; i < element.length; i++) { if (element[i] == e) return i; } return -1; }; u.compMgr.regComp({ comp: u.Autocomplete, compAsString: 'u.Autocomplete', css: 'u-autocomplete' });