/*! * jQuery UI Widget 1.8.21 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Widget */ (function( $, undefined ) { // jQuery 1.4+ if ( $.cleanData ) { var _cleanData = $.cleanData; $.cleanData = function( elems ) { for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { try { $( elem ).triggerHandler( "remove" ); // http://bugs.jquery.com/ticket/8235 } catch( e ) {} } _cleanData( elems ); }; } else { var _remove = $.fn.remove; $.fn.remove = function( selector, keepData ) { return this.each(function() { if ( !keepData ) { if ( !selector || $.filter( selector, [ this ] ).length ) { $( "*", this ).add( [ this ] ).each(function() { try { $( this ).triggerHandler( "remove" ); // http://bugs.jquery.com/ticket/8235 } catch( e ) {} }); } } return _remove.call( $(this), selector, keepData ); }); }; } $.widget = function( name, base, prototype ) { var namespace = name.split( "." )[ 0 ], fullName; name = name.split( "." )[ 1 ]; fullName = namespace + "-" + name; if ( !prototype ) { prototype = base; base = $.Widget; } // create selector for plugin $.expr[ ":" ][ fullName ] = function( elem ) { return !!$.data( elem, name ); }; $[ namespace ] = $[ namespace ] || {}; $[ namespace ][ name ] = function( options, element ) { // allow instantiation without initializing for simple inheritance if ( arguments.length ) { this._createWidget( options, element ); } }; var basePrototype = new base(); // we need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from // $.each( basePrototype, function( key, val ) { // if ( $.isPlainObject(val) ) { // basePrototype[ key ] = $.extend( {}, val ); // } // }); basePrototype.options = $.extend( true, {}, basePrototype.options ); $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { namespace: namespace, widgetName: name, widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, widgetBaseClass: fullName }, prototype ); $.widget.bridge( name, $[ namespace ][ name ] ); }; $.widget.bridge = function( name, object ) { $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", args = Array.prototype.slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init options = !isMethodCall && args.length ? $.extend.apply( null, [ true, options ].concat(args) ) : options; // prevent calls to internal methods if ( isMethodCall && options.charAt( 0 ) === "_" ) { return returnValue; } if ( isMethodCall ) { this.each(function() { var instance = $.data( this, name ), methodValue = instance && $.isFunction( instance[options] ) ? instance[ options ].apply( instance, args ) : instance; // TODO: add this back in 1.9 and use $.error() (see #5972) // if ( !instance ) { // throw "cannot call methods on " + name + " prior to initialization; " + // "attempted to call method '" + options + "'"; // } // if ( !$.isFunction( instance[options] ) ) { // throw "no such method '" + options + "' for " + name + " widget instance"; // } // var methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { returnValue = methodValue; return false; } }); } else { this.each(function() { var instance = $.data( this, name ); if ( instance ) { instance.option( options || {} )._init(); } else { $.data( this, name, new object( options, this ) ); } }); } return returnValue; }; }; $.Widget = function( options, element ) { // allow instantiation without initializing for simple inheritance if ( arguments.length ) { this._createWidget( options, element ); } }; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", options: { disabled: false }, _createWidget: function( options, element ) { // $.widget.bridge stores the plugin instance, but we do it anyway // so that it's stored even before the _create function runs $.data( element, this.widgetName, this ); this.element = $( element ); this.options = $.extend( true, {}, this.options, this._getCreateOptions(), options ); var self = this; this.element.bind( "remove." + this.widgetName, function() { self.destroy(); }); this._create(); this._trigger( "create" ); this._init(); }, _getCreateOptions: function() { return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; }, _create: function() {}, _init: function() {}, destroy: function() { this.element .unbind( "." + this.widgetName ) .removeData( this.widgetName ); this.widget() .unbind( "." + this.widgetName ) .removeAttr( "aria-disabled" ) .removeClass( this.widgetBaseClass + "-disabled " + "ui-state-disabled" ); }, widget: function() { return this.element; }, option: function( key, value ) { var options = key; if ( arguments.length === 0 ) { // don't return a reference to the internal hash return $.extend( {}, this.options ); } if (typeof key === "string" ) { if ( value === undefined ) { return this.options[ key ]; } options = {}; options[ key ] = value; } this._setOptions( options ); return this; }, _setOptions: function( options ) { var self = this; $.each( options, function( key, value ) { self._setOption( key, value ); }); return this; }, _setOption: function( key, value ) { this.options[ key ] = value; if ( key === "disabled" ) { this.widget() [ value ? "addClass" : "removeClass"]( this.widgetBaseClass + "-disabled" + " " + "ui-state-disabled" ) .attr( "aria-disabled", value ); } return this; }, enable: function() { return this._setOption( "disabled", false ); }, disable: function() { return this._setOption( "disabled", true ); }, _trigger: function( type, event, data ) { var prop, orig, callback = this.options[ type ]; data = data || {}; event = $.Event( event ); event.type = ( type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type ).toLowerCase(); // the original event may come from any element // so we need to reset the target on the new event event.target = this.element[ 0 ]; // copy original event properties over to the new event orig = event.originalEvent; if ( orig ) { for ( prop in orig ) { if ( !( prop in event ) ) { event[ prop ] = orig[ prop ]; } } } this.element.trigger( event, data ); return !( $.isFunction(callback) && callback.call( this.element[0], event, data ) === false || event.isDefaultPrevented() ); } }; })( jQuery ); /*! * Marco Polo v1.8.1 * * A jQuery autocomplete plugin for the discerning developer. * * https://github.com/jstayton/jquery-marcopolo * * Copyright 2014 by Justin Stayton * Licensed MIT */ (function (factory) { 'use strict'; // Register as an AMD module, compatible with script loaders like RequireJS. // Source: https://github.com/umdjs/umd/blob/master/jqueryPlugin.js if (typeof define === 'function' && define.amd) { define(['jquery'], factory); } else { factory(jQuery); } }(function ($, undefined) { 'use strict'; // The cache spans all instances and is indexed by URL. This allows different // instances to pull the same cached results if their URLs match. var cache = {}; // jQuery UI's Widget Factory provides an object-oriented plugin framework // that handles the common plumbing tasks. $.widget('mp.marcoPolo', { // Default options. options: { // Whether to cache query results. cache: true, // Whether to compare the selected item against items displayed in the // results list. The selected item is highlighted if a match is found, // instead of the first item in the list ('highlight' option must be // enabled). Set this option to 'true' if the data is a string; // otherwise, specify the data object attribute name to compare on. compare: false, // Additional data to be sent in the request query string. data: {}, // The number of milliseconds to delay before firing a request after a // change is made to the input value. delay: 250, // Format the raw data that's returned from the ajax request. Useful for // further filtering the data or returning the array of results that's // embedded deeper in the object. formatData: null, // Format the text that's displayed when the ajax request fails. Setting // this option to 'null' or returning 'false' suppresses the message from // being displayed. formatError: function () { return 'Your search could not be completed at this time.'; }, // Format the display of each item in the results list. formatItem: function (data) { return data.title || data.name; }, // Format the text that's displayed when the minimum number of characters // (specified with the 'minChars' option) hasn't been reached. Setting // this option to 'null' or returning 'false' suppresses the message from // being displayed. formatMinChars: function (minChars) { return 'Your search must be at least ' + minChars + ' characters.'; }, // Format the text that's displayed when there are no results returned // for the requested input value. Setting this option to 'null' or // returning 'false' suppresses the message from being displayed. formatNoResults: function (q) { return 'No results for ' + q + '.'; }, // Whether to hide the results list when an item is selected. The results // list is still hidden when the input is blurred for any other reason. hideOnSelect: true, // Whether to automatically highlight an item when the results list is // displayed. Usually it's the first item, but it could be the previously // selected item if 'compare' is specified. highlight: true, // Positioning a label over an input is a common design pattern // (sometimes referred to as 'overlabel') that unfortunately doesn't // work so well with all of the input focus/blur events that occur with // autocomplete. With this option, however, the hiding/showing of the // label is handled internally to provide a built-in solution to the // problem. label: null, // The minimum number of characters required before a request is fired. minChars: 1, // Called when the user is finished interacting with the autocomplete // interface, not just the text input, which loses and gains focus on a // results list mouse click. onBlur: null, // Called when the input value changes. onChange: null, // Called when the ajax request fails. onError: null, // Called when the input field receives focus. onFocus: null, // Called when the minimum number of characters (specified with the // 'minChars' option) hasn't been reached by the end of the 'delay'. onMinChars: null, // Called when there are no results returned for the request. onNoResults: null, // Called before the ajax request is made. onRequestBefore: null, // Called after the ajax request completes (success or error). onRequestAfter: null, // Called when there are results to be displayed. onResults: null, // Called when an item is selected from the results list or passed in // through the 'selected' option. onSelect: function (data) { this.val(data.title || data.name); }, // The name of the query string parameter that is set with the input // value. param: 'q', // Whether to clear the input value when no selection is made from the // results list. required: false, // The list items to make selectable. selectable: '*', // Prime the input with a selected item. selected: null, // Whether to allow the browser's default behavior of submitting the form // on ENTER. submitOnEnter: false, // The URL to GET request for the results. url: null }, // Key code to key name mapping for easy reference. keys: { DOWN: 40, END: 35, ENTER: 13, ESC: 27, HOME: 36, TAB: 9, UP: 38 }, // Initialize the plugin on an input. _create: function () { var self = this, $input; // Create a more appropriately named alias for the input. self.$input = $input = self.element.addClass('mp_input'); // The existing input name or a created one. Used for building the ID of // other elements. self.inputName = 'mp_' + ($input.attr('name') || $.now()); // Create an empty list for displaying future results. Insert it directly // after the input element. self.$list = $('
    ') .attr({ 'aria-atomic': 'true', 'aria-busy': 'false', 'aria-live': 'polite', 'id': self.inputName + '_list', 'role': 'listbox' }) .hide() .insertAfter(self.$input); // Remember original input attribute values for when 'destroy' is called // and the input is returned to its original state. self.inputOriginals = { 'aria-activedescendant': $input.attr('aria-activedescendant'), 'aria-autocomplete': $input.attr('aria-autocomplete'), 'aria-expanded': $input.attr('aria-expanded'), 'aria-labelledby': $input.attr('aria-labelledby'), 'aria-owns': $input.attr('aria-owns'), 'aria-required': $input.attr('aria-required'), 'autocomplete': $input.attr('autocomplete'), 'role': $input.attr('role') }; // Set plugin-specific attributes. $input.attr({ 'aria-autocomplete': 'list', 'aria-owns': self.$list.attr('id'), 'autocomplete': 'off', 'role': 'combobox' }); // The ajax request to get results is stored in case the request needs to // be aborted before a response is returned. self.ajax = null; self.ajaxAborted = false; // A reference to this function is maintained for unbinding in the // 'destroy' method. This is necessary because the selector is so // generic (document). self.documentMouseup = null; // "Pseudo" focus includes any interaction with the plugin, even if the // input has blurred. self.focusPseudo = false; // "Real" focus is strictly when the input has focus. self.focusReal = false; // Whether a mousedown event is triggered on a list item. self.mousedown = false; // The currently selected data. self.selectedData = null; // Whether the last selection was by mouseup. self.selectedMouseup = false; // The request buffer timer in case the timer needs to be aborted due to // another key press. self.timer = null; // The current input value for comparison. self.value = self.$input.val(); // Bind the necessary events. self ._bindInput() ._bindList() ._bindDocument(); self ._initSelected() ._initOptions(); }, // Set an option. _setOption: function (option, value) { // Required call to the parent where the new option value is saved. $.Widget.prototype._setOption.apply(this, arguments); this._initOptions(option, value); }, // Initialize options that require a little extra work. _initOptions: function (option, value) { var self = this, allOptions = option === undefined, options = {}; // If no option is specified, initialize all options. if (allOptions) { options = self.options; } // Otherwise, initialize only the specified option. else { options[option] = value; } $.each(options, function (option, value) { switch (option) { case 'label': // Ensure that the 'label' is a jQuery object if a selector string // or plain DOM element is passed. self.options.label = $(value).addClass('mp_label'); // Ensure that the label has an ID for ARIA support. if (self.options.label.attr('id')) { self.removeLabelId = false; } else { self.removeLabelId = true; self.options.label.attr('id', self.inputName + '_label'); } self._toggleLabel(); self.$input.attr('aria-labelledby', self.options.label.attr('id')); break; case 'required': self.$input.attr('aria-required', value); break; case 'selected': // During initial creation (when all options are initialized), only // initialize the 'selected' value if there is one. The // '_initSelected' method parses the input's attributes for a // selected value. if (allOptions && value) { self.select(value, null, true); } break; case 'url': // If no 'url' option is specified, use the parent form's 'action'. if (!value) { self.options.url = self.$input.closest('form').attr('action'); } break; } }); return self; }, // Programmatically change the input value without triggering a search // request (use the 'search' method for that). If the value is different // than the current input value, the 'onChange' callback is fired. change: function (q, onlyValue) { var self = this; // Change the input value if a new value is specified. if (q === self.value) { return; } if (q !== self.$input.val()) { self.$input.val(q); } // Reset the currently selected data. self.selectedData = null; // Keep track of the new input value for later comparison. self.value = q; self._trigger('change', [q]); if (onlyValue !== true) { if (self.focusPseudo) { // Clear out the existing results to prevent future stale results // in case the change is made while the input has focus. self ._cancelPendingRequest() ._hideAndEmptyList(); } else { // Show or hide the label depending on if the input has a value. self._toggleLabel(); } } }, // Programmatically trigger a search request using the existing input value // or a new one. search: function (q) { var $input = this.$input; // Change the input value if a new value is specified. Otherwise, use the // existing input value. if (q !== undefined) { $input.val(q); } // Focus on the input to start the request and enable keyboard // navigation (only available when the input has focus). $input.focus(); }, // Select an item from the results list. select: function (data, $item, initial) { var self = this, $input = self.$input, hideOnSelect = self.options.hideOnSelect; if (hideOnSelect) { self._hideList(); } // If there's no data, consider this a call to deselect (or reset) the // current value. if (!data) { return self.change(''); } // Save the selected data for later reference. self.selectedData = data; self._trigger('select', [data, $item, !!initial]); // It's common to update the input value with the selected item during // 'onSelect', so check if that has occurred and store the new value. if ($input.val() !== self.value) { self.value = $input.val(); // Check if the label needs to be toggled when this method is called // programmatically (usually meaning the input doesn't have focus). if (!self.focusPseudo) { self._toggleLabel(); } // Hide and empty the existing results to prevent future stale results. self._hideAndEmptyList(); } }, // Initialize the input with a selected value from the 'data-selected' // attribute (JSON) or standard 'value' attribute (string). _initSelected: function () { var self = this, $input = self.$input, data = $input.data('selected'), value = $input.val(); if (data) { self.select(data, null, true); } else if (value) { self.select(value, null, true); } return self; }, // Get the currently selected data. selected: function () { return this.selectedData; }, // Remove the autocomplete functionality and return the selected input // fields to their original state. destroy: function () { var self = this, options = self.options, $input = self.$input; // Remove the results list element. self.$list.remove(); // Reset the input to its original attribute values. $.each(self.inputOriginals, function (attribute, value) { if (value === undefined) { $input.removeAttr(attribute); } else { $input.attr(attribute, value); } }); $input.removeClass('mp_input'); // Reset the label to its original state. if (options.label) { options.label.removeClass('mp_label'); if (self.removeLabelId) { options.label.removeAttr('id'); } } // Remove the specific document 'mouseup' event for this instance. $(document).unbind('mouseup.marcoPolo', self.documentMouseup); // Parent destroy removes the input's data and events. $.Widget.prototype.destroy.apply(self, arguments); }, // Get the results list element. list: function () { return this.$list; }, // Bind the necessary events to the input. _bindInput: function () { var self = this, $input = self.$input, $list = self.$list; $input .bind('focus.marcoPolo', function () { // Do nothing if the input already has focus. This prevents // additional 'focus' events from initiating the same request. if (self.focusReal) { return; } // It's overly complicated to check if an input field has focus, so // "manually" keep track in the 'focus' and 'blur' events. self.focusPseudo = true; self.focusReal = true; self._toggleLabel(); // If this focus is the result of a mouse selection (which re-focuses // on the input), ignore as if a blur never occurred. if (self.selectedMouseup) { self.selectedMouseup = false; } // For everything else, initiate a request. else { self._trigger('focus'); self._request($input.val()); } }) .bind('keydown.marcoPolo', function (key) { var $highlighted = $(); switch (key.which) { // Highlight the previous item. case self.keys.UP: // The default moves the cursor to the beginning or end of the // input value. Keep it in its current place. key.preventDefault(); // Show the list if it has been hidden by ESC. self ._showList() ._highlightPrev(); break; // Highlight the next item. case self.keys.DOWN: // The default moves the cursor to the beginning or end of the // input value. Keep it in its current place. key.preventDefault(); // Show the list if it has been hidden by ESC. self ._showList() ._highlightNext(); break; // Highlight the first item. case self.keys.HOME: // Show the list if it has been hidden by ESC. self ._showList() ._highlightFirst(); break; // Highlight the last item. case self.keys.END: // Show the list if it has been hidden by ESC. self ._showList() ._highlightLast(); break; // Select the currently highlighted item. Input keeps focus. case self.keys.ENTER: // Prevent selection if the list isn't visible. if (!$list.is(':visible')) { // Prevent the form from submitting. if (!self.options.submitOnEnter) { key.preventDefault(); } return; } $highlighted = self._highlighted(); if ($highlighted.length) { self.select($highlighted.data('marcoPolo'), $highlighted); } // Prevent the form from submitting if 'submitOnEnter' is // disabled or if there's a highlighted item. if (!self.options.submitOnEnter || $highlighted.length) { key.preventDefault(); } break; // Select the currently highlighted item. Input loses focus. case self.keys.TAB: // Prevent selection if the list isn't visible. if (!$list.is(':visible')) { return; } $highlighted = self._highlighted(); if ($highlighted.length) { self.select($highlighted.data('marcoPolo'), $highlighted); } break; // Hide the list. case self.keys.ESC: self ._cancelPendingRequest() ._hideList(); break; } }) .bind('keyup.marcoPolo', function () { // Check if the input value has changed. This prevents keys like CTRL // and SHIFT from firing a new request. if ($input.val() !== self.value) { self._request($input.val()); } }) .bind('blur.marcoPolo', function () { self.focusReal = false; // When an item in the results list is clicked, the input blur event // fires before the click event, causing the results list to become // hidden (code below). This 1ms timeout ensures that the click event // code fires before that happens. setTimeout(function () { // If the $list 'mousedown' event has fired without a 'mouseup' // event, wait for that before dismissing everything. if (!self.mousedown) { self._dismiss(); } }, 1); }); return self; }, // Bind the necessary events to the list. _bindList: function () { var self = this; self.$list .bind('mousedown.marcoPolo', function () { // Tracked for use in the input 'blur' event. self.mousedown = true; }) .delegate('li.mp_selectable', 'mouseover', function () { self._addHighlight($(this)); }) .delegate('li.mp_selectable', 'mouseout', function () { self._removeHighlight($(this)); }) .delegate('li.mp_selectable', 'mouseup', function () { var $item = $(this); self.select($item.data('marcoPolo'), $item); // This event is tracked so that when 'focus' is called on the input // (below), a new request isn't fired. self.selectedMouseup = true; // Give focus back to the input for easy tabbing on to the next // field. self.$input.focus(); }); return self; }, // Bind the necessary events to the document. _bindDocument: function () { var self = this; // A reference to this function is maintained for unbinding in the // 'destroy' method. This is necessary because the selector is so // generic (document). $(document).bind('mouseup.marcoPolo', self.documentMouseup = function () { // Tracked for use in the input 'blur' event. self.mousedown = false; // Ensure that everything is dismissed if anything other than the input // is clicked. (A click on a selectable list item is handled above, // before this code fires.) if (!self.focusReal && self.$list.is(':visible')) { self._dismiss(); } }); return self; }, // Show or hide the label (if one exists) depending on whether the input // has focus or a value. _toggleLabel: function () { var self = this, $label = self.options.label; if ($label && $label.length) { if (self.focusPseudo || self.$input.val()) { $label.hide(); } else { $label.show(); } } return self; }, // Get the first selectable item in the results list. _firstSelectableItem: function () { return this.$list.children('li.mp_selectable:visible:first'); }, // Get the last selectable item in the results list. _lastSelectableItem: function () { return this.$list.children('li.mp_selectable:visible:last'); }, // Get the currently highlighted item in the results list. _highlighted: function () { return this.$list.children('li.mp_highlighted'); }, // Remove the highlight class from the specified item. _removeHighlight: function ($item) { $item .removeClass('mp_highlighted') .attr('aria-selected', 'false') .removeAttr('id'); this.$input.removeAttr('aria-activedescendant'); return this; }, // Add the highlight class to the specified item. _addHighlight: function ($item) { // The current highlight is removed to ensure that only one item is // highlighted at a time. this._removeHighlight(this._highlighted()); $item .addClass('mp_highlighted') .attr({ 'aria-selected': 'true', 'id': this.inputName + '_highlighted' }); this.$input.attr('aria-activedescendant', $item.attr('id')); return this; }, // Highlight the first selectable item in the results list. _highlightFirst: function () { this._addHighlight(this._firstSelectableItem()); return this; }, // Highlight the last selectable item in the results list. _highlightLast: function () { this._addHighlight(this._lastSelectableItem()); return this; }, // Highlight the item before the currently highlighted item. _highlightPrev: function () { var $highlighted = this._highlighted(), $prev = $highlighted.prevAll('li.mp_selectable:visible:first'); // If there is no "previous" selectable item, continue at the list's end. if (!$prev.length) { $prev = this._lastSelectableItem(); } this._addHighlight($prev); return this; }, // Highlight the item after the currently highlighted item. _highlightNext: function () { var $highlighted = this._highlighted(), $next = $highlighted.nextAll('li.mp_selectable:visible:first'); // If there is no "next" selectable item, continue at the list's // beginning. if (!$next.length) { $next = this._firstSelectableItem(); } this._addHighlight($next); return this; }, // Show the results list. _showList: function () { // But only if there are results to be shown. if (this.$list.children().length) { this.$list.show(); this.$input.attr('aria-expanded', 'true'); } return this; }, // Hide the results list. _hideList: function () { this.$list.hide(); this.$input .removeAttr('aria-activedescendant') .removeAttr('aria-expanded'); return this; }, // Empty the results list. _emptyList: function () { this.$list.empty(); this.$input.removeAttr('aria-activedescendant'); return this; }, // Hide and empty the results list. _hideAndEmptyList: function () { this.$list .hide() .empty(); this.$input .removeAttr('aria-activedescendant') .removeAttr('aria-expanded'); return this; }, // Build the results list from a successful request that returned no data. _buildNoResultsList: function (q) { var self = this, $input = self.$input, $list = self.$list, options = self.options, $item = $('
  1. '); formatItem = options.formatItem.call($input, datum, $item); // Store the original data for easy access later. $item.data('marcoPolo', datum); $item .html(formatItem) .appendTo($list); if (compare && options.highlight) { // If the 'compare' setting is set to boolean 'true', assume the data // is a string and compare directly. if (options.compare === true) { compareCurrent = datum; compareSelected = selected; } // Otherwise, assume the data is an object and the 'compare' setting // is the attribute name to compare on. else { compareCurrent = datum[options.compare]; compareSelected = selected[options.compare]; } // Highlight this item if it matches the selected item. if (compareCurrent === compareSelected) { self._addHighlight($item); // Stop comparing the remaining results, as a match has been made. compare = false; compareMatch = true; } } } // Mark all selectable items, based on the 'selectable' selector setting. $list .children(options.selectable) .addClass('mp_selectable') .attr({ 'aria-selected': 'false', 'role': 'option' }); self._trigger('results', [data]); self._showList(); // Highlight the first item in the results list if the currently selected // item was not found and already highlighted, and the option to auto- // highlight is enabled. if (!compareMatch && options.highlight) { self._highlightFirst(); } return self; }, // Build the results list from a successful request. _buildSuccessList: function (q, data) { var self = this, $input = self.$input, options = self.options; self._emptyList(); // Fire 'formatData' callback. if (options.formatData) { data = options.formatData.call($input, data); } if (!data || data.length === 0 || $.isEmptyObject(data)) { self._buildNoResultsList(q); } else { self._buildResultsList(q, data); } return self; }, // Build the results list with an error message. _buildErrorList: function (jqXHR, textStatus, errorThrown) { var self = this, $input = self.$input, $list = self.$list, options = self.options, $item = $('