| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013 | /** * Bootstrap Search Suggest * @desc    这是一个基于 bootstrap 按钮式下拉菜单组件的搜索建议插件,必须使用于按钮式下拉菜单组件上。 * @author  renxia <lzwy0820#qq.com> * @github  https://github.com/lzwme/bootstrap-suggest-plugin.git * @since   2014-10-09 *=============================================================================== * (c) Copyright 2014-2016 http://lzw.me All Rights Reserved. ********************************************************************************/(function (factory) {    if (typeof define === "function" && define.amd) {        define(['jquery'], factory);    } else if (typeof exports === 'object' && typeof module === 'object') {        factory(require('jquery'));    } else if (window.jQuery) {        factory(window.jQuery);    } else {        throw new Error('Not found jQuery.');    }})(function($) {    var VERSION = 'VERSION_PLACEHOLDER';    var $window = $(window);    var isIe = 'ActiveXObject' in window; // 用于对 IE 的兼容判断    var inputLock; // 用于中文输入法输入时锁定搜索    // ie 下和 chrome 51 以上浏览器版本,出现滚动条时不计算 padding    var chromeVer = navigator.userAgent.match(/Chrome\/(\d+)/);    if (chromeVer) {        chromeVer = +chromeVer[1];    }    var notNeedCalcPadding = isIe || chromeVer > 51;    // 一些常量    var BSSUGGEST = 'bsSuggest';    var onDataRequestSuccess = 'onDataRequestSuccess';    var DISABLED = 'disabled';    var TRUE = true;    var FALSE = false;    /**     * 错误处理     */    function handleError(e1, e2) {        if (!window.console || !window.console.trace) {            return;        }        console.trace(e1);        if (e2) {            console.trace(e2);        }    }    /**     * 获取当前 tr 列的关键字数据     */    function getPointKeyword($list) {        return $list.data();    }    /**     * 设置或获取输入框的 alt 值     */    function setOrGetAlt($input, val) {        return val !== undefined ? $input.attr('alt', val) : $input.attr('alt');    }    /**     * 设置或获取输入框的 data-json 值     */    function setJson($input, val) {        return val !== undefined ? $input.attr('data-json', val) : $input.attr('data-json');    }    /**     * 设置或获取输入框的 data-id 值     */    function setOrGetDataId($input, val) {        return val !== (void 0) ? $input.attr('data-id', val) : $input.attr('data-id');    }    /**     * 设置选中的值     */    function setValue($input, keywords, options) {        if (!keywords || !keywords.key) {            return;        }        var separator = options.separator || ',',            inputValList,            inputIdList,            dataId = setOrGetDataId($input);        if (options && options.multiWord) {            inputValList = $input.val().split(separator);            inputValList[inputValList.length - 1] = keywords.key;            //多关键字检索支持设置id --- 存在 bug,不建议使用            if (!dataId) {                inputIdList = [keywords.id];            } else {                inputIdList = dataId.split(separator);                inputIdList.push(keywords.id);            }            setOrGetDataId($input, inputIdList.join(separator))                .val(inputValList.join(separator))                .focus();        } else {            setOrGetDataId($input, keywords.id || '').val(keywords.key).focus();        }        $input.data('pre-val', $input.val())            .trigger('onSetSelectValue', [keywords, (options.data.value || options._lastData.value)[keywords.index]]);    }    /**     * 调整选择菜单位置     * @param {Object} $input     * @param {Object} $dropdownMenu     * @param {Object} options     */    function adjustDropMenuPos($input, $dropdownMenu, options) {        if (!$dropdownMenu.is(':visible')) {            return;        }        var $parent = $input.parent();        var parentHeight = $parent.height();        var parentWidth = $parent.width();        if (options.autoDropup) {            setTimeout(function() {                var offsetTop = $input.offset().top;                var winScrollTop = $window.scrollTop();                var menuHeight = $dropdownMenu.height();                if ( // 自动判断菜单向上展开                    ($window.height() + winScrollTop - offsetTop) < menuHeight && // 假如向下会撑长页面                    offsetTop > (menuHeight + winScrollTop) // 而且向上不会撑到顶部                ) {                    $parent.addClass('dropup');                } else {                    $parent.removeClass('dropup');                }            }, 10);        }        // 列表对齐方式        var dmcss = {};        if (options.listAlign === 'left') {            dmcss = {                'left': $input.siblings('div').width() - parentWidth,                'right': 'auto'            };        } else if (options.listAlign === 'right') {            dmcss = {                'left': 'auto',                'right': 0            };        }        // ie 下,不显示按钮时的 top/bottom        if (isIe && !options.showBtn) {            if (!$parent.hasClass('dropup')) {                dmcss.top = parentHeight;                dmcss.bottom = 'auto';            } else {                dmcss.top = 'auto';                dmcss.bottom = parentHeight;            }        }        // 是否自动最小宽度        if (!options.autoMinWidth) {            dmcss.minWidth = parentWidth;        }        /* else {            dmcss['width'] = 'auto';        }*/        $dropdownMenu.css(dmcss);        return $input;    }    /**     * 设置输入框背景色     * 当设置了 indexId,而输入框的 data-id 为空时,输入框加载警告色     */    function setBackground($input, options) {        var inputbg, bg, warnbg;        if ((options.indexId === -1 && !options.idField) || options.multiWord) {            return $input;        }        bg = options.inputBgColor;        warnbg = options.inputWarnColor;        var curVal = $input.val();        var preVal = $input.data('pre-val');        if (setOrGetDataId($input) || !curVal) {            $input.css('background', bg || '');            if (!curVal && preVal) {                $input.trigger('onUnsetSelectValue').data('pre-val', '');            }            return $input;        }        inputbg = $input.css('backgroundColor').replace(/ /g, '').split(',', 3).join(',');        // 自由输入的内容,设置背景色        if (!~warnbg.indexOf(inputbg)) {            $input.trigger('onUnsetSelectValue') // 触发取消data-id事件                .data('pre-val', '')                .css('background', warnbg);        }        return $input;    }    /**     * 调整滑动条     */    function adjustScroll($input, $dropdownMenu, options) {        // 控制滑动条        var $hover = $input.parent().find('tbody tr.' + options.listHoverCSS),            pos, maxHeight;        if ($hover.length) {            pos = ($hover.index() + 3) * $hover.height();            maxHeight = +$dropdownMenu.css('maxHeight').replace('px', '');            if (pos > maxHeight || $dropdownMenu.scrollTop() > maxHeight) {                pos = pos - maxHeight;            } else {                pos = 0;            }            $dropdownMenu.scrollTop(pos);        }    }    /**     * 解除所有列表 hover 样式     */    function unHoverAll($dropdownMenu, options) {        $dropdownMenu.find('tr.' + options.listHoverCSS).removeClass(options.listHoverCSS);    }    /**     * 验证 $input 对象是否符合条件     *   1. 必须为 bootstrap 下拉式菜单     *   2. 必须未初始化过     */    function checkInput($input, $dropdownMenu, options) {        if (            !$dropdownMenu.length || // 过滤非 bootstrap 下拉式菜单对象            $input.data(BSSUGGEST) // 是否已经初始化的检测        ) {            return FALSE;        }        $input.data(BSSUGGEST, {            options: options        });        return TRUE;    }    /**     * 数据格式检测     * 检测 ajax 返回成功数据或 data 参数数据是否有效     * data 格式:{"value": [{}, {}...]}     */    function checkData(data) {        var isEmpty = TRUE, o;        for (o in data) {            if (o === 'value') {                isEmpty = FALSE;                break;            }        }        if (isEmpty) {            handleError('返回数据格式错误!');            return FALSE;        }        if (!data.value.length) {            // handleError('返回数据为空!');            return FALSE;        }        return data;    }    /**     * 判断字段名是否在 options.effectiveFields 配置项中     * @param  {String} field   要判断的字段名     * @param  {Object} options     * @return {Boolean}        effectiveFields 为空时始终返回 true     */    function inEffectiveFields(field, options) {        var effectiveFields = options.effectiveFields;        return !(field === '__index' ||            effectiveFields.length &&            !~$.inArray(field, effectiveFields));    }    /**     * 判断字段名是否在 options.searchFields 搜索字段配置中     */    function inSearchFields(field, options) {        return ~$.inArray(field, options.searchFields);    }    /**     * 通过下拉菜单显示提示文案     */    function showTip(tip, $input, $dropdownMenu, options) {        $dropdownMenu.html('<div style="padding:10px 5px 5px">' + tip + '</div>').show();        adjustDropMenuPos($input, $dropdownMenu, options);    }    /**     * 下拉列表刷新     * 作为 fnGetData 的 callback 函数调用     */    function refreshDropMenu($input, data, options) {        var $dropdownMenu = $input.parent().find('ul:eq(0)'),            len, i, field, index = 0,            tds,            html = ['<table class="table table-condensed table-sm" style="margin:0">'],            idValue, keyValue; // 作为输入框 data-id 和内容的字段值        var dataList = data.value;        if (!data || !(len = dataList.length)) {            if (options.emptyTip) {                showTip(options.emptyTip, $input, $dropdownMenu, options);            } else {                $dropdownMenu.empty().hide();            }            return $input;        }        // 相同数据,不用继续渲染了        if (            options._lastData &&            JSON.stringify(options._lastData) === JSON.stringify(data) &&            $dropdownMenu.find('tr').length === len        ) {            $dropdownMenu.show();            return adjustDropMenuPos($input, $dropdownMenu, options);            // return $input;        }        options._lastData = data;        // 生成表头        if (options.showHeader) {            html.push('<thead><tr>');            for (field in dataList[0]) {                if (!inEffectiveFields(field, options)) {                    continue;                }                html.push('<th>', (options.effectiveFieldsAlias[field] || field),                    index === 0 ? ('(' + len + ')') : '' , // 表头第一列记录总数                    '</th>');                index++;            }            html.push('</tr></thead>');        }        html.push('<tbody>');        // console.log(data, len);        // 按列加数据        var dataI;        for (i = 0; i < len; i++) {            index = 0;            tds = [];            dataI = dataList[i];            idValue = dataI[options.idField] || '';            keyValue = dataI[options.keyField] || '';            for (field in dataI) {                // 标记作为 value 和 作为 id 的值                if (!keyValue && options.indexKey === index) {                    keyValue = dataI[field];                }                if (!idValue && options.indexId === index) {                    idValue = dataI[field];                }                index++;                // 列表中只显示有效的字段                if (inEffectiveFields(field, options)) {                    tds.push('<td data-name="', field, '">', dataI[field], '</td>');                }            }            html.push('<tr data-index="', (dataI.__index || i),                '" data-id="', idValue,                '" data-key="', keyValue,                 '" data-json="', encodeURIComponent(JSON.stringify(dataI)), '">',                tds.join(''), '</tr>');        }        html.push('</tbody></table>');        $dropdownMenu.html(html.join('')).show();        // scrollbar 存在时,延时到动画结束时调整 padding        setTimeout(function() {            if (notNeedCalcPadding) {                return;            }            var $table = $dropdownMenu.find('table:eq(0)'),                pdr = 0,                mgb = 0;            if (                $dropdownMenu.height() < $table.height() &&                +$dropdownMenu.css('minWidth').replace('px', '') < $dropdownMenu.width()            ) {                pdr = 18;                mgb = 20;            }            $dropdownMenu.css('paddingRight', pdr);            $table.css('marginBottom', mgb);        }, 301);        adjustDropMenuPos($input, $dropdownMenu, options);        return $input;    }    /**     * ajax 获取数据     * @param  {Object} options     * @return {Object}         $.Deferred     */    function ajax(options, keyword) {        keyword = keyword || '';        var preAjax = options._preAjax;        if (preAjax && preAjax.abort && preAjax.readyState !== 4) {            // console.log('abort pre ajax');            preAjax.abort();        }        var ajaxParam = {            type: 'GET',            dataType: options.jsonp ? 'jsonp' : 'json',            timeout: 5000,        };        // jsonp        if (options.jsonp) {            ajaxParam.jsonp = options.jsonp;        }        // 自定义 ajax 请求参数生成方法        var adjustAjaxParam,            fnAdjustAjaxParam = options.fnAdjustAjaxParam;        if ($.isFunction(fnAdjustAjaxParam)) {            adjustAjaxParam = fnAdjustAjaxParam(keyword, options);            // options.fnAdjustAjaxParam 返回false,则终止 ajax 请求            if (FALSE === adjustAjaxParam) {                return;            }            $.extend(ajaxParam, adjustAjaxParam);        }        // url 调整        ajaxParam.url = function() {            if (!keyword || ajaxParam.data) {                return ajaxParam.url || options.url;            }            var type = '?';            if (/=$/.test(options.url)) {                type = '';            } else if (/\?/.test(options.url)) {                type = '&';            }            return options.url + type + encodeURIComponent(keyword);        }();        return options._preAjax = $.ajax(ajaxParam).done(function(result) {            options.data = options.fnProcessData(result);        }).fail(function(err) {            if (options.fnAjaxFail) {                options.fnAjaxFail(err, options);            }        });    }    /**     * 检测 keyword 与 value 是否存在互相包含     * @param  {String}  keyword 用户输入的关键字     * @param  {String}  key     匹配字段的 key     * @param  {String}  value   key 字段对应的值     * @param  {Object}  options     * @return {Boolean}         包含/不包含     */    function isInWord(keyword, key, value, options) {        value = $.trim(value);        if (options.ignorecase) {            keyword = keyword.toLocaleLowerCase();            value = value.toLocaleLowerCase();        }        return value &&            (inEffectiveFields(key, options) || inSearchFields(key, options)) && // 必须在有效的搜索字段中            (                ~value.indexOf(keyword) || // 匹配值包含关键字                options.twoWayMatch && ~keyword.indexOf(value) // 关键字包含匹配值            );    }    /**     * 通过 ajax 或 json 参数获取数据     */    function getData(keyword, $input, callback, options) {        var data, validData, filterData = {                value: []            },            i, key, len,            fnPreprocessKeyword = options.fnPreprocessKeyword;        keyword = keyword || '';        // 获取数据前对关键字预处理方法        if ($.isFunction(fnPreprocessKeyword)) {            keyword = fnPreprocessKeyword(keyword, options);        }        // 给了url参数,则从服务器 ajax 请求        // console.log(options.url + keyword);        if (options.url) {            var timer;            if (options.searchingTip) {                timer = setTimeout(function() {                    showTip(options.searchingTip, $input, $input.parent().find('ul'), options);                }, 600);            }            ajax(options, keyword).done(function(result) {                callback($input, options.data, options); // 为 refreshDropMenu                $input.trigger(onDataRequestSuccess, result);                if (options.getDataMethod === 'firstByUrl') {                    options.url = null;                }            }).always(function() {                timer && clearTimeout(timer);            });        } else {            // 没有给出 url 参数,则从 data 参数获取            data = options.data;            validData = checkData(data);            // 本地的 data 数据,则在本地过滤            if (validData) {                if (keyword) {                    // 输入不为空时则进行匹配                    len = data.value.length;                    for (i = 0; i < len; i++) {                        for (key in data.value[i]) {                            if (                                data.value[i][key] &&                                isInWord(keyword, key, data.value[i][key] + '', options)                            ) {                                filterData.value.push(data.value[i]);                                filterData.value[filterData.value.length - 1].__index = i;                                break;                            }                        }                    }                } else {                    filterData = data;                }            }            callback($input, filterData, options);        } // else    }    /**     * 数据处理     * url 获取数据时,对数据的处理,作为 fnGetData 之后的回调处理     */    function processData(data) {        return checkData(data);    }    /**     * 取得 clearable 清除按钮     */    function getIClear($input, options) {        var $iClear = $input.prev('i.clearable');        // 是否可清除已输入的内容(添加清除按钮)        if (options.clearable && !$iClear.length) {                $iClear = $('<i class="clearable glyphicon glyphicon-remove"></i>')                    .prependTo($input.parent());        }        return $iClear.css({            position: 'absolute',            top: 12,            right: options.showBtn ? ($input.next('.input-group-btn').width() || 33) + 2 : 12,            zIndex: 4,            cursor: 'pointer',            fontSize: 12        }).hide();    }    /**     * 默认的配置选项     * @type {Object}     */    var defaultOptions = {        url: null,                      // 请求数据的 URL 地址        jsonp: null,                    // 设置此参数名,将开启jsonp功能,否则使用json数据结构        data: {            value: []        },                              // 提示所用的数据,注意格式        indexId: 0,                     // 每组数据的第几个数据,作为input输入框的 data-id,设为 -1 且 idField 为空则不设置此值        indexKey: 0,                    // 每组数据的第几个数据,作为input输入框的内容        idField: '',                    // 每组数据的哪个字段作为 data-id,优先级高于 indexId 设置(推荐)        keyField: '',                   // 每组数据的哪个字段作为输入框内容,优先级高于 indexKey 设置(推荐)        /* 搜索相关 */        autoSelect: TRUE,               // 键盘向上/下方向键时,是否自动选择值        allowNoKeyword: TRUE,           // 是否允许无关键字时请求数据        getDataMethod: 'firstByUrl',    // 获取数据的方式,url:一直从url请求;data:从 options.data 获取;firstByUrl:第一次从Url获取全部数据,之后从options.data获取        delayUntilKeyup: FALSE,         // 获取数据的方式 为 firstByUrl 时,是否延迟到有输入时才请求数据        ignorecase: FALSE,              // 前端搜索匹配时,是否忽略大小写        effectiveFields: [],            // 有效显示于列表中的字段,非有效字段都会过滤,默认全部有效。        effectiveFieldsAlias: {},       // 有效字段的别名对象,用于 header 的显示        searchFields: [],               // 有效搜索字段,从前端搜索过滤数据时使用,但不一定显示在列表中。effectiveFields 配置字段也会用于搜索过滤        twoWayMatch: TRUE,              // 是否双向匹配搜索。为 true 即输入关键字包含或包含于匹配字段均认为匹配成功,为 false 则输入关键字包含于匹配字段认为匹配成功        multiWord: FALSE,               // 以分隔符号分割的多关键字支持        separator: ',',                 // 多关键字支持时的分隔符,默认为半角逗号        delay: 300,                     // 搜索触发的延时时间间隔,单位毫秒        emptyTip: '',                   // 查询为空时显示的内容,可为 html        searchingTip: '搜索中...',      // ajax 搜索时显示的提示内容,当搜索时间较长时给出正在搜索的提示        /* UI */        autoDropup: FALSE,              // 选择菜单是否自动判断向上展开。设为 true,则当下拉菜单高度超过窗体,且向上方向不会被窗体覆盖,则选择菜单向上弹出        autoMinWidth: FALSE,            // 是否自动最小宽度,设为 false 则最小宽度不小于输入框宽度        showHeader: FALSE,              // 是否显示选择列表的 header。为 true 时,有效字段大于一列则显示表头        showBtn: TRUE,                  // 是否显示下拉按钮        inputBgColor: '',               // 输入框背景色,当与容器背景色不同时,可能需要该项的配置        inputWarnColor: 'rgba(255,0,0,.1)', // 输入框内容不是下拉列表选择时的警告色        listStyle: {            'padding-top': 0,            'max-height': '375px',            'max-width': '800px',            'overflow': 'auto',            'width': 'auto',            'transition': '0.3s',            '-webkit-transition': '0.3s',            '-moz-transition': '0.3s',            '-o-transition': '0.3s'        },                              // 列表的样式控制        listAlign: 'left',              // 提示列表对齐位置,left/right/auto        listHoverStyle: 'background: #07d; color:#fff', // 提示框列表鼠标悬浮的样式        listHoverCSS: 'jhover',         // 提示框列表鼠标悬浮的样式名称        clearable: FALSE,               // 是否可清除已输入的内容        /* key */        keyLeft: 37,                    // 向左方向键,不同的操作系统可能会有差别,则自行定义        keyUp: 38,                      // 向上方向键        keyRight: 39,                   // 向右方向键        keyDown: 40,                    // 向下方向键        keyEnter: 13,                   // 回车键        /* methods */        fnProcessData: processData,     // 格式化数据的方法,返回数据格式参考 data 参数        fnGetData: getData,             // 获取数据的方法,无特殊需求一般不作设置        fnAdjustAjaxParam: null,        // 调整 ajax 请求参数方法,用于更多的请求配置需求。如对请求关键字作进一步处理、修改超时时间等        fnPreprocessKeyword: null,      // 搜索过滤数据前,对输入关键字作进一步处理方法。注意,应返回字符串        fnAjaxFail: null,               // ajax 失败时回调方法    };    var methods = {        init: function(options) {            // 参数设置            var self = this;            options = options || {};            // 默认配置有效显示字段多于一个,则显示列表表头,否则不显示            if (undefined === options.showHeader && options.effectiveFields && options.effectiveFields.length > 1) {                options.showHeader = TRUE;            }            options = $.extend(TRUE, {}, defaultOptions, options);            // 旧的方法兼容            if (options.processData) {                options.fnProcessData = options.processData;            }            if (options.getData) {                options.fnGetData = options.getData;            }            if (options.getDataMethod === 'firstByUrl' && options.url && !options.delayUntilKeyup) {                ajax(options).done(function(result) {                    options.url = null;                    self.trigger(onDataRequestSuccess, result);                });            }            // 鼠标滑动到条目样式            if (!$('#' + BSSUGGEST).length) {                $('head:eq(0)').append('<style id="' + BSSUGGEST + '">.' + options.listHoverCSS + '{' + options.listHoverStyle + '}</style>');            }            return self.each(function() {                var $input = $(this),                    $parent = $input.parent(),                    $iClear = getIClear($input, options),                    isMouseenterMenu,                    keyupTimer, // keyup 与 input 事件延时定时器                    $dropdownMenu = $parent.find('ul:eq(0)');                // 验证输入框对象是否符合条件                if (!checkInput($input, $dropdownMenu, options)) {                    console.warn('不是一个标准的 bootstrap 下拉式菜单或已初始化:', $input);                    return;                }                // 是否显示 button 按钮                if (!options.showBtn) {                    $input.css('borderRadius', 4);                    $parent.css('width', '100%')                        .find('.btn:eq(0)').hide();                }                // 移除 disabled 类,并禁用自动完成                $input.removeClass(DISABLED).prop(DISABLED, FALSE).attr('autocomplete', 'off');                // dropdown-menu 增加修饰                $dropdownMenu.css(options.listStyle);                // 默认背景色                if (!options.inputBgColor) {                    options.inputBgColor = $input.css('backgroundColor');                }                // 开始事件处理                $input.on('keydown', function(event) {                    var currentList, tipsKeyword; // 提示列表上被选中的关键字                    // 当提示层显示时才对键盘事件处理                    if (!$dropdownMenu.is(':visible')) {                        setOrGetDataId($input, '');                        return;                    }                    currentList = $dropdownMenu.find('.' + options.listHoverCSS);                    tipsKeyword = ''; // 提示列表上被选中的关键字                    unHoverAll($dropdownMenu, options);                    if (event.keyCode === options.keyDown) { // 如果按的是向下方向键                        if (!currentList.length) {                            // 如果提示列表没有一个被选中,则将列表第一个选中                            tipsKeyword = getPointKeyword($dropdownMenu.find('tbody tr:first').mouseover());                        } else if (!currentList.next().length) {                            // 如果是最后一个被选中,则取消选中,即可认为是输入框被选中,并恢复输入的值                            if (options.autoSelect) {                                setOrGetDataId($input, '').val(setOrGetAlt($input));                            }                        } else {                            // 选中下一行                            tipsKeyword = getPointKeyword(currentList.next().mouseover());                        }                        // 控制滑动条                        adjustScroll($input, $dropdownMenu, options);                        if (!options.autoSelect) {                            return;                        }                    } else if (event.keyCode === options.keyUp) { // 如果按的是向上方向键                        if (!currentList.length) {                            tipsKeyword = getPointKeyword($dropdownMenu.find('tbody tr:last').mouseover());                        } else if (!currentList.prev().length) {                            if (options.autoSelect) {                                setOrGetDataId($input, '').val(setOrGetAlt($input));                            }                        } else {                            // 选中前一行                            tipsKeyword = getPointKeyword(currentList.prev().mouseover());                        }                        // 控制滑动条                        adjustScroll($input, $dropdownMenu, options);                        if (!options.autoSelect) {                            return;                        }                    } else if (event.keyCode === options.keyEnter) {                        tipsKeyword = getPointKeyword(currentList);                        $dropdownMenu.hide(); // .empty();                    } else {                        setOrGetDataId($input, '');                    }                    // 设置值 tipsKeyword                    // console.log(tipsKeyword);                    setValue($input, tipsKeyword, options);                }).on('compositionstart', function(event) {                    // 中文输入开始,锁定                    // console.log('compositionstart');                    inputLock = TRUE;                }).on('compositionend', function(event) {                    // 中文输入结束,解除锁定                    // console.log('compositionend');                    inputLock = FALSE;                }).on('keyup input paste', function(event) {                    var word;                    if (event.keyCode) {                        setBackground($input, options);                    }                    // 如果弹起的键是回车、向上或向下方向键则返回                    if (~$.inArray(event.keyCode, [options.keyDown, options.keyUp, options.keyEnter])) {                        $input.val($input.val()); // 让鼠标输入跳到最后                        return;                    }                    clearTimeout(keyupTimer);                    keyupTimer = setTimeout(function() {                        // console.log('input keyup', event);                        // 锁定状态,返回                        if (inputLock) {                            return;                        }                        word = $input.val();                        // 若输入框值没有改变则返回                        if ($.trim(word) && word === setOrGetAlt($input)) {                            return;                        }                        // 当按下键之前记录输入框值,以方便查看键弹起时值有没有变                        setOrGetAlt($input, word);                        if (options.multiWord) {                            word = word.split(options.separator).reverse()[0];                        }                        // 是否允许空数据查询                        if (!word.length && !options.allowNoKeyword) {                            return;                        }												if (options.url) {                        	options.fnGetData($.trim(word), $input, refreshDropMenu, options);                        }                    }, options.delay || 300);                }).on('focus', function() {                    // console.log('input focus');                    adjustDropMenuPos($input, $dropdownMenu, options);                }).on('blur', function() {                    if (!isMouseenterMenu) { // 不是进入下拉列表状态,则隐藏列表                        $dropdownMenu.css('display', '');                    }                }).on('click', function() {                    // console.log('input click');                    var word = $input.val();                    if (                        $.trim(word) &&                        word === setOrGetAlt($input) &&                        $dropdownMenu.find('table tr').length                    ) {                        return $dropdownMenu.show();                    }                    // if ($dropdownMenu.css('display') !== 'none') {                    if ($dropdownMenu.is(':visible')) {                        return;                    }                    if (options.multiWord) {                        word = word.split(options.separator).reverse()[0];                    }                    // 是否允许空数据查询                    if (!word.length && !options.allowNoKeyword) {                        return;                    }                    // console.log('word', word);                    options.fnGetData($.trim(word), $input, refreshDropMenu, options);                });                // 下拉按钮点击时                $parent.find('.btn:eq(0)').attr('data-toggle', '').click(function() {                    var display = 'none';                    // if ($dropdownMenu.is(':visible')) {                    if ($dropdownMenu.css('display') === display) {                        display = 'block';                        if (options.url) {                            $input.click().focus();                            if (!$dropdownMenu.find('tr').length) {                                display = 'none';                            }                        } else {                            // 不以 keyword 作为过滤,展示所有的数据                            refreshDropMenu($input, options.data, options);                        }                    }                    $dropdownMenu.css('display', display);                    return FALSE;                });                // 列表中滑动时,输入框失去焦点                $dropdownMenu.mouseenter(function() {                        // console.log('mouseenter')                        isMouseenterMenu = 1;                        $input.blur();                    }).mouseleave(function() {                        // console.log('mouseleave')                        isMouseenterMenu = 0;                        $input.focus();                    }).on('mouseenter', 'tbody tr', function() {                        // 行上的移动事件                        unHoverAll($dropdownMenu, options);                        $(this).addClass(options.listHoverCSS);                        return FALSE; // 阻止冒泡                    })                    .on('mousedown', 'tbody tr', function() {                        var keywords = getPointKeyword($(this));                        setValue($input, keywords, options);                        setOrGetAlt($input, keywords.key);                        setJson($input, keywords.json);                        setBackground($input, options);                        $dropdownMenu.hide();                    });                // 存在清空按钮                if ($iClear.length) {                    $iClear.click(function () {                        setOrGetDataId($input, '').val('');                        setBackground($input, options);                    });                    $parent.mouseenter(function() {                        if (!$input.prop(DISABLED)) {                            $iClear.show();                        }                    }).mouseleave(function() {                        $iClear.hide();                    });                }            });        },        show: function() {            return this.each(function() {                $(this).click();            });        },        hide: function() {            return this.each(function() {                $(this).parent().find('ul:eq(0)').css('display', '');            });        },        disable: function() {            return this.each(function() {                $(this).attr(DISABLED, TRUE)                    .parent().find('.btn:eq(0)').prop(DISABLED, TRUE);            });        },        enable: function() {            return this.each(function() {                $(this).attr(DISABLED, FALSE)                    .parent().find('.btn:eq(0)').prop(DISABLED, FALSE);            });        },        destroy: function() {            return this.each(function() {                $(this).off().removeData(BSSUGGEST).removeAttr('style')                    .parent().find('.btn:eq(0)').off().show().attr('data-toggle', 'dropdown').prop(DISABLED, FALSE) // .addClass(DISABLED);                    .next().css('display', '').off();            });        },        version: function() {            return VERSION;        }    };    $.fn[BSSUGGEST] = function(options) {        // 方法判断        if (typeof options === 'string' && methods[options]) {            var inited = TRUE;            this.each(function() {                if (!$(this).data(BSSUGGEST)) {                    return inited = FALSE;                }            });            // 只要有一个未初始化,则全部都不执行方法,除非是 init 或 version            if (!inited && 'init' !== options && 'version' !== options) {                return this;            }            // 如果是方法,则参数第一个为函数名,从第二个开始为函数参数            return methods[options].apply(this, [].slice.call(arguments, 1));        } else {            // 调用初始化方法            return methods.init.apply(this, arguments);        }    }});
 |