/** * jQuery TextExt Plugin * http://textextjs.com * * @version 1.3.1 * @copyright Copyright (C) 2011 Alex Gorbatchev. All rights reserved. * @license MIT License */ (function($) { /** * AJAX plugin is very useful if you want to load list of items from a data point and pass it * to the Autocomplete or Filter plugins. * * Because it meant to be as a helper method for either Autocomplete or Filter plugin, without * either of these two present AJAX plugin won't do anything. * * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax */ function TextExtAjax() {}; $.fn.textext.TextExtAjax = TextExtAjax; $.fn.textext.addPlugin('ajax', TextExtAjax); var p = TextExtAjax.prototype, /** * AJAX plugin options are grouped under `ajax` when passed to the `$().textext()` function. Be * mindful that the whole `ajax` object is also passed to jQuery `$.ajax` call which means that * you can change all jQuery options as well. Please refer to the jQuery documentation on how * to set url and all other parameters. For example: * * $('textarea').textext({ * plugins: 'ajax', * ajax: { * url: 'http://...' * } * }) * * **Important**: Because it's necessary to pass options to `jQuery.ajax()` in a single object, * all jQuery related AJAX options like `url`, `dataType`, etc **must** be within the `ajax` object. * This is the exception to general rule that TextExt options can be specified in dot or camel case * notation. * * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax.options */ /** * By default, when user starts typing into the text input, AJAX plugin will start making requests * to the `url` that you have specified and will pass whatever user has typed so far as a parameter * named `q`, eg `?q=foo`. * * If you wish to change this behaviour, you can pass a function as a value for this option which * takes one argument (the user input) and should return a key/value object that will be converted * to the request parameters. For example: * * 'dataCallback' : function(query) * { * return { 'search' : query }; * } * * @name ajax.data.callback * @default null * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax.options.data.callback */ OPT_DATA_CALLBACK = 'ajax.data.callback', /** * By default, the server end point is constantly being reloaded whenever user changes the value * in the text input. If you'd rather have the client do result filtering, you can return all * possible results from the server and cache them on the client by setting this option to `true`. * * In such a case, only one call to the server will be made and filtering will be performed on * the client side using `ItemManager` attached to the core. * * @name ajax.data.results * @default false * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax.options.cache.results */ OPT_CACHE_RESULTS = 'ajax.cache.results', /** * The loading message delay is set in seconds and will specify how long it would take before * user sees the message. If you don't want user to ever see this message, set the option value * to `Number.MAX_VALUE`. * * @name ajax.loading.delay * @default 0.5 * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax.options.loading.delay */ OPT_LOADING_DELAY = 'ajax.loading.delay', /** * Whenever an AJAX request is made and the server takes more than the number of seconds specified * in `ajax.loading.delay` to respond, the message specified in this option will appear in the drop * down. * * @name ajax.loading.message * @default "Loading..." * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.options.loading.message */ OPT_LOADING_MESSAGE = 'ajax.loading.message', /** * When user is typing in or otherwise changing the value of the text input, it's undesirable to make * an AJAX request for every keystroke. Instead it's more conservative to send a request every number * of seconds while user is typing the value. This number of seconds is specified by the `ajax.type.delay` * option. * * @name ajax.type.delay * @default 0.5 * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.options.type.delay */ OPT_TYPE_DELAY = 'ajax.type.delay', /** * AJAX plugin dispatches or reacts to the following events. * * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.events */ /** * AJAX plugin reacts to the `getSuggestions` event dispatched by the Autocomplete plugin. * * @name getSuggestions * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.events.getSuggestions */ /** * In the event of successful AJAX request, the AJAX coponent dispatches the `setSuggestions` * event meant to be recieved by the Autocomplete plugin. * * @name setSuggestions * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.events.setSuggestions */ EVENT_SET_SUGGESTION = 'setSuggestions', /** * AJAX plugin dispatches the `showDropdown` event which Autocomplete plugin is expecting. * This is used to temporarily show the loading message if the AJAX request is taking longer * than expected. * * @name showDropdown * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.events.showDropdown */ EVENT_SHOW_DROPDOWN = 'showDropdown', TIMER_LOADING = 'loading', DEFAULT_OPTS = { ajax : { typeDelay : 0.5, loadingMessage : 'Loading...', loadingDelay : 0.5, cacheResults : false, dataCallback : null } } ; /** * Initialization method called by the core during plugin instantiation. * * @signature TextExtAjax.init(core) * * @param core {TextExt} Instance of the TextExt core class. * * @author agorbatchev * @date 2011/08/17 * @id TextExtAjax.init */ p.init = function(core) { var self = this; self.baseInit(core, DEFAULT_OPTS); self.on({ getSuggestions : self.onGetSuggestions }); self._suggestions = null; }; /** * Performas an async AJAX with specified options. * * @signature TextExtAjax.load(query) * * @param query {String} Value that user has typed into the text area which is * presumably the query. * * @author agorbatchev * @date 2011/08/14 * @id TextExtAjax.load */ p.load = function(query) { var self = this, dataCallback = self.opts(OPT_DATA_CALLBACK) || function(query) { return { q : query } }, opts ; opts = $.extend(true, { data : dataCallback(query), success : function(data) { self.onComplete(data, query) }, error : function(jqXHR, message) { console.error(message, query) } }, self.opts('ajax') ); $.ajax(opts); }; /** * Successful call AJAX handler. Takes the data that came back from AJAX and the * original query that was used to make the call. * * @signature TextExtAjax.onComplete(data, query) * * @param data {Object} Data loaded from the server, should be an Array of strings * by default or whatever data structure your custom `ItemManager` implements. * * @param query {String} Query string, ie whatever user has typed in. * * @author agorbatchev * @date 2011/08/14 * @id TextExtAjax.onComplete */ p.onComplete = function(data, query) { var self = this, result = data ; self.dontShowLoading(); // If results are expected to be cached, then we store the original // data set and return the filtered one based on the original query. // That means we do filtering on the client side, instead of the // server side. if(self.opts(OPT_CACHE_RESULTS) == true) { self._suggestions = data; result = self.itemManager().filter(data, query); } self.trigger(EVENT_SET_SUGGESTION, { result : result }); }; /** * If show loading message timer was started, calling this function disables it, * otherwise nothing else happens. * * @signature TextExtAjax.dontShowLoading() * * @author agorbatchev * @date 2011/08/16 * @id TextExtAjax.dontShowLoading */ p.dontShowLoading = function() { this.stopTimer(TIMER_LOADING); }; /** * Shows message specified in `ajax.loading.message` if loading data takes more than * number of seconds specified in `ajax.loading.delay`. * * @signature TextExtAjax.showLoading() * * @author agorbatchev * @date 2011/08/15 * @id TextExtAjax.showLoading */ p.showLoading = function() { var self = this; self.dontShowLoading(); self.startTimer( TIMER_LOADING, self.opts(OPT_LOADING_DELAY), function() { self.trigger(EVENT_SHOW_DROPDOWN, function(autocomplete) { autocomplete.clearItems(); var node = autocomplete.addDropdownItem(self.opts(OPT_LOADING_MESSAGE)); node.addClass('text-loading'); }); } ); }; /** * Reacts to the `getSuggestions` event and begin loading suggestions. If * `ajax.cache.results` is specified, all calls after the first one will use * cached data and filter it with the `core.itemManager.filter()`. * * @signature TextExtAjax.onGetSuggestions(e, data) * * @param e {Object} jQuery event. * @param data {Object} Data structure passed with the `getSuggestions` event * which contains the user query, eg `{ query : "..." }`. * * @author agorbatchev * @date 2011/08/15 * @id TextExtAjax.onGetSuggestions */ p.onGetSuggestions = function(e, data) { var self = this, suggestions = self._suggestions, query = (data || {}).query || '' ; if(suggestions && self.opts(OPT_CACHE_RESULTS) === true) return self.onComplete(suggestions, query); self.startTimer( 'ajax', self.opts(OPT_TYPE_DELAY), function() { self.showLoading(); self.load(query); } ); }; })(jQuery);