Advertisement
Guest User

Ricardo Rodrigues

a guest
Nov 24th, 2009
787
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*!
  2. * Auto Complete 4.1
  3. * October 5, 2009
  4. * Corey Hart @ http://www.codenothing.com
  5. * Ricardo Rodrigues @ http://sharpdevpt.blogspot.com
  6. */
  7. ; (function($, undefined) {
  8.     // Expose autoComplete to the jQuery chain
  9.     $.fn.autoComplete = function() {
  10.         // Force array of arguments
  11.         var args = Array.prototype.slice.call(arguments);
  12.  
  13.         // Autocomplete special triggers
  14.         if (typeof args[0] === 'string')
  15.         // Trigger the requested function, and dont break the chain!
  16.             return $(this).trigger('autoComplete.' + args.shift(), args);
  17.  
  18.         // Initiate the autocomplete
  19.         return autoComplete.call(this, args[0]);
  20.     };
  21.  
  22.     // bgiframe is needed to fix z-index problem for IE6 users.
  23.     $.fn.bgiframe = $.fn.bgiframe ? $.fn.bgiframe : $.fn.bgIframe ? $.fn.bgIframe : function() {
  24.         // For applications that don't have bgiframe plugin installed, create a useless
  25.         // function that doesn't break the chain
  26.         return this;
  27.     };
  28.  
  29.     // Autocomplete function
  30.     var inputIndex = 0, autoComplete = function(options) {
  31.         return this.each(function() {
  32.             // Cache objects
  33.             var $input = $(this).attr('autocomplete', 'off'), $li, timeid, timeid2, blurid,
  34.             // Internal Per Input Cache
  35.                 cache = {
  36.                     length: 0,
  37.                     val: undefined,
  38.                     list: {}
  39.                 },
  40.             // Set defaults and include metadata support
  41.                 settings = $.extend({
  42.                     // Inner Function Defaults (Best to leave alone)
  43.                     opt: -1,
  44.                     inputval: undefined,
  45.                     mouseClick: false,
  46.                     dataName: 'ac-data',
  47.                     inputIndex: ++inputIndex,
  48.                     // Server Script Path
  49.                     ajax: 'ajax.php',
  50.                     dataSupply: [],
  51.                     dataFn: undefined,
  52.                     // Drop List CSS
  53.                     list: 'auto-complete-list',
  54.                     rollover: 'auto-complete-list-rollover',
  55.                     width: $input.outerWidth(),
  56.                     // Post Data
  57.                     postVar: 'value',
  58.                     postData: {},
  59.                     // Limitations
  60.                     minChars: 1,
  61.                     maxItems: -1,
  62.                     maxRequests: 0,
  63.                     requestType: 'post',
  64.                     requests: 0, // Inner Function Default
  65.                     // Events
  66.                     onMaxRequest: function() { },
  67.                     onSelect: function() { },
  68.                     onRollover: function() { },
  69.                     onBlur: function() { },
  70.                     onFocus: function() { },
  71.                     inputControl: function(v) { return v; },
  72.                     preventEnterSubmit: false,
  73.                     enter: true, // Inner Function Default
  74.                     delay: 100,
  75.                     selectFuncFire: true, // Inner Function Default
  76.                     // Caching Options
  77.                     useCache: true,
  78.                     cacheLimit: 50,
  79.                     htmlCustomFormatter: undefined
  80.                 }, options || {}, $.metadata ? $input.metadata() : {}),
  81.  
  82.             // Create the drop list (Use an existing one if possible)
  83.                 $ul = $('ul.' + settings.list)[0] ?
  84.                     $('ul.' + settings.list).bgiframe() :
  85.                     $('<ul/>').appendTo('body').addClass(settings.list).bgiframe().hide();
  86.  
  87.             // Input Events
  88.             $input.data('ac-input-index', settings.inputIndex) // Attach input index
  89.             // Central autoComplete specific function
  90.             .bind('keyup.autoComplete', function(event) {
  91.                 var key = event.keyCode;
  92.                 settings.mouseClick = false;
  93.  
  94.                 // Enter Key
  95.                 if (key == 13 && $li) {
  96.                     settings.opt = -1;
  97.                     // Ensure the select function only gets fired once
  98.                     if (settings.selectFuncFire) {
  99.                         settings.selectFuncFire = false;
  100.                         settings.onSelect.call($input[0], $li.data(settings.dataName), $li, $ul);
  101.                         if (timeid2) clearTimeout(timeid2);
  102.                         timeid2 = setTimeout(function() { settings.selectFuncFire = true; }, 1000);
  103.                     }
  104.                     $ul.hide();
  105.                 }
  106.                 // Up Arrow
  107.                 else if (key == 38) {
  108.                     if (settings.opt > 0) {
  109.                         settings.opt--;
  110.                         $li = $('li', $ul).removeClass(settings.rollover).eq(settings.opt).addClass(settings.rollover);
  111.                         $input.val($li.data(settings.dataName).value || '');
  112.                         settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul);
  113.                     } else {
  114.                         settings.opt = -1;
  115.                         $input.val(settings.inputval);
  116.                         $ul.hide();
  117.                     }
  118.                 }
  119.                 // Down Arrow
  120.                 else if (key == 40) {
  121.                     if (settings.opt < $('li', $ul).length - 1) {
  122.                         settings.opt++;
  123.                         $li = $('li', $ul.show()).removeClass(settings.rollover).eq(settings.opt).addClass(settings.rollover);
  124.                         $input.val($li.data(settings.dataName).value || '');
  125.                         settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul);
  126.                     }
  127.                 }
  128.                 // Everything else is possible input
  129.                 else {
  130.                     settings.opt = -1;
  131.                     settings.inputval = $input.val();
  132.                     cache.val = settings.inputControl.call($input, settings.inputval, key);
  133.                     if (cache.val.length >= settings.minChars) {
  134.                         // Send request on timer so fast typing doesn't overload requests
  135.                         if (timeid) clearTimeout(timeid);
  136.                         timeid = setTimeout(function() { sendRequest(settings, cache); clearTimeout(timeid); }, settings.delay);
  137.                     } else if (key == 8) { // Remove list on backspace of small string
  138.                         $ul.html('').hide();
  139.                     }
  140.                 }
  141.             })
  142.             // Bind specific Blur Actions
  143.             .bind('blur.autoComplete', function() {
  144.                 settings.enter = true;
  145.                 blurid = setTimeout(function() {
  146.                     if (settings.mouseClick)
  147.                         return false;
  148.                     settings.opt = -1;
  149.                     settings.onBlur.call($input[0], settings.inputval, $ul);
  150.                     $ul.hide();
  151.                 }, 150);
  152.             })
  153.             // Bind specific focus actions
  154.             .bind('focus.autoComplete', function() {
  155.                 settings.enter = false;
  156.                 // If ul is not associated with current input, clear it
  157.                 if (settings.inputIndex != $ul.data('ac-input-index'))
  158.                     $ul.html('').hide();
  159.                 settings.onFocus.call($input[0], $ul);
  160.             })
  161.  
  162.             /**
  163.             * Autocomplete Special Triggers
  164.             * -Extensions off autoComplete event
  165.             */
  166.             // Allows for change of settings at any point
  167.             .bind('autoComplete.settings', function(event, newSettings) {
  168.                 // Give access to current settings and cache
  169.                 if ($.isFunction(newSettings)) {
  170.                     var ret = newSettings.call($input[0], settings, cache);
  171.                     // Allow for extending of settings/cache based off function return values
  172.                     if ($.isArray(ret) && ret.length) {
  173.                         settings = $.extend(true, {}, settings, ret[0] || settings);
  174.                         cache = $.extend(true, {}, cache, ret[1] || cache);
  175.                     }
  176.                 } else {
  177.                     // Extend deep so settings are kept
  178.                     settings = $.extend(true, {}, settings, newSettings || {});
  179.                 }
  180.             })
  181.             // Clears the Cache & requests (requests can be blocked on request)
  182.             .bind('autoComplete.flush', function(event, cacheOnly) {
  183.                 cache = { length: 0, val: undefined, list: {} };
  184.                 if (!cacheOnly) settings.requests = 0;
  185.             })
  186.             // External button trigger for ajax requests
  187.             .bind('autoComplete.button.ajax', function(event, postData, cacheName) {
  188.                 // Refocus the input box
  189.                 $input.focus();
  190.                 // Remove blur trigger
  191.                 if (blurid) clearTimeout(blurid);
  192.                 // Allow for just passing the cache name
  193.                 if (typeof postData === 'string') {
  194.                     cacheName = postData;
  195.                     postData = {};
  196.                 }
  197.                 // If no cache name is given, supply a non-common word
  198.                 cache.val = cacheName || 'NON_404_<>!@$^&';
  199.                 // Send request on timer so focus event doesn't override
  200.                 if (timeid) clearTimeout(timeid);
  201.                 timeid = setTimeout(function() {
  202.                     sendRequest($.extend(true, {}, settings, { opt: -1, maxItems: -1, postData: postData || {} }), cache);
  203.                     clearTimeout(timeid);
  204.                 }, settings.delay);
  205.             })
  206.             // External button trigger for supplied data
  207.             .bind('autoComplete.button.supply', function(event, data, cacheName) {
  208.                 // Refocus the input box
  209.                 $input.focus();
  210.                 // Remove blur trigger
  211.                 if (blurid) clearTimeout(blurid);
  212.                 // Allow for just passing of cacheName
  213.                 if (typeof data === 'string') {
  214.                     cacheName = data;
  215.                     data = undefined;
  216.                 }
  217.                 // If no cache name is given, supply a non-common word
  218.                 cache.val = cacheName || 'NON_404_SUPPLY_<>!@$^&';
  219.                 // If no data is supplied, use data in settings
  220.                 data = $.isArray(data) ? data : settings.dataSupply;
  221.                 // Send request on timer so focus event doesn't override
  222.                 if (timeid) clearTimeout(timeid);
  223.                 timeid = setTimeout(function() {
  224.                     sendRequest($.extend(true, {}, settings, { opt: -1, maxItems: -1, dataSupply: data, dataFn: function() { return true; } }), cache);
  225.                     clearTimeout(timeid);
  226.                 }, settings.delay);
  227.             })
  228.             // Add a destruction function
  229.             .bind('autoComplete.destroy', function() {
  230.                 // Unbind input events
  231.                 $input.unbind('keyup.autoComplete blur.autoComplete focus.autoComplete autoComplete')
  232.                 // Unbind the form submission event
  233.                     .parents('form').eq(0).unbind('submit.autoComplete.' + settings.inputIndex);
  234.             })
  235.             // Add a show list function
  236.             .bind('autoComplete.showlist', function() {
  237.                 $ul.show();
  238.             })
  239.             // Back to normal events
  240.             // Prevent form submission if defined in settings
  241.             .parents('form').eq(0).bind('submit.autoComplete.' + settings.inputIndex, function() {
  242.                 return settings.preventEnterSubmit ? settings.enter : true;
  243.             });
  244.  
  245.             // Ajax/Cache Request
  246.             function sendRequest(settings, cache) {
  247.                 // Check Max reqests first
  248.                 if (settings.maxRequests && ++settings.requests >= settings.maxRequests)
  249.                     return settings.requests > settings.maxRequests ?
  250.                         false : settings.onMaxRequest.call($input[0], settings.inputval, $ul);
  251.  
  252.                 // Load from cache if possible
  253.                 if (settings.useCache && cache.list[cache.val])
  254.                     return loadResults(cache.list[cache.val], settings, cache);
  255.  
  256.                 // Use user supplied data when defined
  257.                 if (settings.dataSupply.length)
  258.                     return userSuppliedData(settings, cache);
  259.  
  260.                 // Send request server side
  261.                 settings.postData[settings.postVar] = cache.val
  262.                 $[settings.requestType](settings.ajax, settings.postData, function(json) {
  263.                     // Show the list if there is a return, else hide it
  264.                     loadResults(json, settings, cache);
  265.                     // Use jQuery's method of json evaluation
  266.                     // (thus, can only send 'get' or 'post' jQuery requests)
  267.                 }, 'json');
  268.             }
  269.  
  270.             // Parse User Supplied Data
  271.             function userSuppliedData(settings, cache) {
  272.                 var json = [], // Result list
  273.                     fn = $.isFunction(settings.dataFn), // User supplied function
  274.                     regex = fn ? undefined : new RegExp('^' + cache.val, 'i'), // Only compile regex if needed
  275.                     k = 0, entry, i; // Looping vars
  276.  
  277.                 // Loop through each entry and find matches
  278.                 for (i in settings.dataSupply) {
  279.                     entry = settings.dataSupply[i];
  280.                     // Force object
  281.                     entry = typeof entry === 'object' && entry.value ? entry : { value: entry };
  282.                     // If user supplied function, use that, otherwise test with default regex
  283.                     if ((fn && settings.dataFn.call($input[0], cache.val, entry.value, json, i, settings.dataSupply)) ||
  284.                             (!fn && entry.value.match(regex))) {
  285.                         // Reduce browser load by breaking on limit if it exists
  286.                         if (settings.maxItems > -1 && ++k > settings.maxItems)
  287.                             break;
  288.                         json.push(entry);
  289.                     }
  290.                 }
  291.                 // Use normal load functionality
  292.                 loadResults(json, settings, cache);
  293.             }
  294.  
  295.             // List Functionality
  296.             function loadResults(list, settings, cache) {
  297.                 // Store results into the cache if need be
  298.                 if (settings.useCache) {
  299.                     cache.length++;
  300.                     cache.list[cache.val] = list;
  301.                     // Clear cache if necessary
  302.                     if (settings.cacheLength > settings.cacheLimit) {
  303.                         cache.list = {};
  304.                         cache.length = 0;
  305.                     }
  306.                 }
  307.  
  308.                 // Ensure there is a list
  309.                 if (!list || list.length < 1)
  310.                     return $ul.html('').hide();
  311.  
  312.                 // Initialize Vars together (save bytes)
  313.                 var offset = $input.offset(), // Store offsets
  314.                     aci = 0, i; // Index list items
  315.  
  316.                 // Clear the List and align it properly
  317.                 $ul.data('ac-input-index', settings.inputIndex).html('').css({
  318.                     top: offset.top + $input.outerHeight(),
  319.                     left: offset.left,
  320.                     width: settings.width
  321.                 });
  322.  
  323.                 // Add new rows to the list
  324.                 for (i in list) {
  325.                     var fn = $.isFunction(settings.htmlCustomFormatter);
  326.                     if (list[i].value || fn) {
  327.                         if (settings.maxItems > -1 && ++aci > settings.maxItems) {
  328.                             break;
  329.                         }
  330.                         var appendHTML = list[i].display || list[i].value;
  331.                         //user custom function to compose HTML
  332.                         if (fn) {
  333.                             appendHTML = settings.htmlCustomFormatter(list[i]);
  334.                         }
  335.                         $('<li/>').appendTo($ul).html(appendHTML)
  336.                             .data(settings.dataName, list[i]).data('ac-index', aci);
  337.                     }
  338.                 }
  339.  
  340.                 // Remove old mouseout event and return orignal val when not hovering
  341.                 $ul.show().unbind('mouseout.autoComplete').bind('mouseout.autoComplete', function() {
  342.                     $('li.' + settings.rollover, $ul).removeClass(settings.rollover);
  343.                     if (!settings.mouseClick && settings.selectFuncFire)
  344.                         $input.val(settings.inputval);
  345.                     // Unbind any events that linger from previous drops
  346.                     // I don't understand why this helps yet, because the old li elements are
  347.                     // removed and new ones created/added to the ul element; but for now, it works
  348.                 }).children('li').unbind('mouseover.autoComplete').unbind('click.autoComplete')
  349.                 // New mouseover and click events
  350.                 .bind('mouseover.autoComplete', function() {
  351.                     $li = $(this);
  352.                     $('li.' + settings.rollover, $ul).removeClass(settings.rollover);
  353.                     $input.val($li.addClass(settings.rollover).data(settings.dataName).value);
  354.                     settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul);
  355.                     settings.opt = $li.data('ac-index');
  356.                 }).bind('click.autoComplete', function() {
  357.                     settings.mouseClick = true;
  358.                     if (blurid) clearTimeout(blurid);
  359.                     settings.onSelect.call($input[0], $li.data(settings.dataName), $li, $ul);
  360.                     $ul.hide();
  361.                     // Bring the focus back to the input when clicking a list member
  362.                     $input.focus();
  363.                 });
  364.             }
  365.         });
  366.     };
  367. })(jQuery);
  368.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement