Tuesday, November 24, 2009

JQuery Autocomplete - more complete!

Hi there!
A couple of days ago I started to develop something that needed a fancy, unobtrusive jquery autocomplete plugin, that could accept JSON as data source and created light HTML and I found this one.
But then, it missed two simple functionalities that I added:

One -> the for each of your result items, isn't controlled by you, it expects that your JSON contains a value property, and uses it, but my JSON was a little more complex than that, so I coded a setting to the autocomplete plugin, so you can define, or not, your drawing function!
Two -> If you needed to force your autocomplete list to show up, you couldn't, and I coded that two.

So here's the result

or here:

/*!
* Auto Complete 4.1
* October 5, 2009
* Corey Hart @ http://www.codenothing.com
* Ricardo Rodrigues @ http://sharpdevpt.blogspot.com
*/
; (function($, undefined) {
    // Expose autoComplete to the jQuery chain
    $.fn.autoComplete = function() {
        // Force array of arguments
        var args = Array.prototype.slice.call(arguments);

        // Autocomplete special triggers
        if (typeof args[0] === 'string')
        // Trigger the requested function, and dont break the chain!
            return $(this).trigger('autoComplete.' + args.shift(), args);

        // Initiate the autocomplete
        return autoComplete.call(this, args[0]);
    };

    // bgiframe is needed to fix z-index problem for IE6 users.
    $.fn.bgiframe = $.fn.bgiframe ? $.fn.bgiframe : $.fn.bgIframe ? $.fn.bgIframe : function() {
        // For applications that don't have bgiframe plugin installed, create a useless 
        // function that doesn't break the chain
        return this;
    };

    // Autocomplete function
    var inputIndex = 0, autoComplete = function(options) {
        return this.each(function() {
            // Cache objects
            var $input = $(this).attr('autocomplete', 'off'), $li, timeid, timeid2, blurid,
            // Internal Per Input Cache
				cache = {
				    length: 0,
				    val: undefined,
				    list: {}
				},
            // Set defaults and include metadata support
				settings = $.extend({
				    // Inner Function Defaults (Best to leave alone)
				    opt: -1,
				    inputval: undefined,
				    mouseClick: false,
				    dataName: 'ac-data',
				    inputIndex: ++inputIndex,
				    // Server Script Path
				    ajax: 'ajax.php',
				    dataSupply: [],
				    dataFn: undefined,
				    // Drop List CSS
				    list: 'auto-complete-list',
				    rollover: 'auto-complete-list-rollover',
				    width: $input.outerWidth(),
				    // Post Data
				    postVar: 'value',
				    postData: {},
				    // Limitations
				    minChars: 1,
				    maxItems: -1,
				    maxRequests: 0,
				    requestType: 'post',
				    requests: 0, // Inner Function Default
				    // Events
				    onMaxRequest: function() { },
				    onSelect: function() { },
				    onRollover: function() { },
				    onBlur: function() { },
				    onFocus: function() { },
				    inputControl: function(v) { return v; },
				    preventEnterSubmit: false,
				    enter: true, // Inner Function Default
				    delay: 100,
				    selectFuncFire: true, // Inner Function Default
				    // Caching Options
				    useCache: true,
				    cacheLimit: 50,
				    htmlCustomFormatter: undefined
				}, options || {}, $.metadata ? $input.metadata() : {}),

            // Create the drop list (Use an existing one if possible)
				$ul = $('ul.' + settings.list)[0] ?
					$('ul.' + settings.list).bgiframe() :
					$('
    ').appendTo('body').addClass(settings.list).bgiframe().hide(); // Input Events $input.data('ac-input-index', settings.inputIndex) // Attach input index // Central autoComplete specific function .bind('keyup.autoComplete', function(event) { var key = event.keyCode; settings.mouseClick = false; // Enter Key if (key == 13 && $li) { settings.opt = -1; // Ensure the select function only gets fired once if (settings.selectFuncFire) { settings.selectFuncFire = false; settings.onSelect.call($input[0], $li.data(settings.dataName), $li, $ul); if (timeid2) clearTimeout(timeid2); timeid2 = setTimeout(function() { settings.selectFuncFire = true; }, 1000); } $ul.hide(); } // Up Arrow else if (key == 38) { if (settings.opt > 0) { settings.opt--; $li = $('li', $ul).removeClass(settings.rollover).eq(settings.opt).addClass(settings.rollover); $input.val($li.data(settings.dataName).value || ''); settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul); } else { settings.opt = -1; $input.val(settings.inputval); $ul.hide(); } } // Down Arrow else if (key == 40) { if (settings.opt < $('li', $ul).length - 1) { settings.opt++; $li = $('li', $ul.show()).removeClass(settings.rollover).eq(settings.opt).addClass(settings.rollover); $input.val($li.data(settings.dataName).value || ''); settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul); } } // Everything else is possible input else { settings.opt = -1; settings.inputval = $input.val(); cache.val = settings.inputControl.call($input, settings.inputval, key); if (cache.val.length >= settings.minChars) { // Send request on timer so fast typing doesn't overload requests if (timeid) clearTimeout(timeid); timeid = setTimeout(function() { sendRequest(settings, cache); clearTimeout(timeid); }, settings.delay); } else if (key == 8) { // Remove list on backspace of small string $ul.html('').hide(); } } }) // Bind specific Blur Actions .bind('blur.autoComplete', function() { settings.enter = true; blurid = setTimeout(function() { if (settings.mouseClick) return false; settings.opt = -1; settings.onBlur.call($input[0], settings.inputval, $ul); $ul.hide(); }, 150); }) // Bind specific focus actions .bind('focus.autoComplete', function() { settings.enter = false; // If ul is not associated with current input, clear it if (settings.inputIndex != $ul.data('ac-input-index')) $ul.html('').hide(); settings.onFocus.call($input[0], $ul); }) /** * Autocomplete Special Triggers * -Extensions off autoComplete event */ // Allows for change of settings at any point .bind('autoComplete.settings', function(event, newSettings) { // Give access to current settings and cache if ($.isFunction(newSettings)) { var ret = newSettings.call($input[0], settings, cache); // Allow for extending of settings/cache based off function return values if ($.isArray(ret) && ret.length) { settings = $.extend(true, {}, settings, ret[0] || settings); cache = $.extend(true, {}, cache, ret[1] || cache); } } else { // Extend deep so settings are kept settings = $.extend(true, {}, settings, newSettings || {}); } }) // Clears the Cache & requests (requests can be blocked on request) .bind('autoComplete.flush', function(event, cacheOnly) { cache = { length: 0, val: undefined, list: {} }; if (!cacheOnly) settings.requests = 0; }) // External button trigger for ajax requests .bind('autoComplete.button.ajax', function(event, postData, cacheName) { // Refocus the input box $input.focus(); // Remove blur trigger if (blurid) clearTimeout(blurid); // Allow for just passing the cache name if (typeof postData === 'string') { cacheName = postData; postData = {}; } // If no cache name is given, supply a non-common word cache.val = cacheName || 'NON_404_<>!@$^&'; // Send request on timer so focus event doesn't override if (timeid) clearTimeout(timeid); timeid = setTimeout(function() { sendRequest($.extend(true, {}, settings, { opt: -1, maxItems: -1, postData: postData || {} }), cache); clearTimeout(timeid); }, settings.delay); }) // External button trigger for supplied data .bind('autoComplete.button.supply', function(event, data, cacheName) { // Refocus the input box $input.focus(); // Remove blur trigger if (blurid) clearTimeout(blurid); // Allow for just passing of cacheName if (typeof data === 'string') { cacheName = data; data = undefined; } // If no cache name is given, supply a non-common word cache.val = cacheName || 'NON_404_SUPPLY_<>!@$^&'; // If no data is supplied, use data in settings data = $.isArray(data) ? data : settings.dataSupply; // Send request on timer so focus event doesn't override if (timeid) clearTimeout(timeid); timeid = setTimeout(function() { sendRequest($.extend(true, {}, settings, { opt: -1, maxItems: -1, dataSupply: data, dataFn: function() { return true; } }), cache); clearTimeout(timeid); }, settings.delay); }) // Add a destruction function .bind('autoComplete.destroy', function() { // Unbind input events $input.unbind('keyup.autoComplete blur.autoComplete focus.autoComplete autoComplete') // Unbind the form submission event .parents('form').eq(0).unbind('submit.autoComplete.' + settings.inputIndex); }) // Add a show list function .bind('autoComplete.showlist', function() { $ul.show(); }) // Back to normal events // Prevent form submission if defined in settings .parents('form').eq(0).bind('submit.autoComplete.' + settings.inputIndex, function() { return settings.preventEnterSubmit ? settings.enter : true; }); // Ajax/Cache Request function sendRequest(settings, cache) { // Check Max reqests first if (settings.maxRequests && ++settings.requests >= settings.maxRequests) return settings.requests > settings.maxRequests ? false : settings.onMaxRequest.call($input[0], settings.inputval, $ul); // Load from cache if possible if (settings.useCache && cache.list[cache.val]) return loadResults(cache.list[cache.val], settings, cache); // Use user supplied data when defined if (settings.dataSupply.length) return userSuppliedData(settings, cache); // Send request server side settings.postData[settings.postVar] = cache.val $[settings.requestType](settings.ajax, settings.postData, function(json) { // Show the list if there is a return, else hide it loadResults(json, settings, cache); // Use jQuery's method of json evaluation // (thus, can only send 'get' or 'post' jQuery requests) }, 'json'); } // Parse User Supplied Data function userSuppliedData(settings, cache) { var json = [], // Result list fn = $.isFunction(settings.dataFn), // User supplied function regex = fn ? undefined : new RegExp('^' + cache.val, 'i'), // Only compile regex if needed k = 0, entry, i; // Looping vars // Loop through each entry and find matches for (i in settings.dataSupply) { entry = settings.dataSupply[i]; // Force object entry = typeof entry === 'object' && entry.value ? entry : { value: entry }; // If user supplied function, use that, otherwise test with default regex if ((fn && settings.dataFn.call($input[0], cache.val, entry.value, json, i, settings.dataSupply)) || (!fn && entry.value.match(regex))) { // Reduce browser load by breaking on limit if it exists if (settings.maxItems > -1 && ++k > settings.maxItems) break; json.push(entry); } } // Use normal load functionality loadResults(json, settings, cache); } // List Functionality function loadResults(list, settings, cache) { // Store results into the cache if need be if (settings.useCache) { cache.length++; cache.list[cache.val] = list; // Clear cache if necessary if (settings.cacheLength > settings.cacheLimit) { cache.list = {}; cache.length = 0; } } // Ensure there is a list if (!list || list.length < 1) return $ul.html('').hide(); // Initialize Vars together (save bytes) var offset = $input.offset(), // Store offsets aci = 0, i; // Index list items // Clear the List and align it properly $ul.data('ac-input-index', settings.inputIndex).html('').css({ top: offset.top + $input.outerHeight(), left: offset.left, width: settings.width }); // Add new rows to the list for (i in list) { var fn = $.isFunction(settings.htmlCustomFormatter); if (list[i].value || fn) { if (settings.maxItems > -1 && ++aci > settings.maxItems) { break; } var appendHTML = list[i].display || list[i].value; //user custom function to compose HTML if (fn) { appendHTML = settings.htmlCustomFormatter(list[i]); } $('
  • ').appendTo($ul).html(appendHTML)
    .data(settings.dataName, list[i]).data('ac-index', aci);
    }
    }

    // Remove old mouseout event and return orignal val when not hovering
    $ul.show().unbind('mouseout.autoComplete').bind('mouseout.autoComplete', function() {
    $('li.' + settings.rollover, $ul).removeClass(settings.rollover);
    if (!settings.mouseClick && settings.selectFuncFire)
    $input.val(settings.inputval);
    // Unbind any events that linger from previous drops
    // I don't understand why this helps yet, because the old li elements are
    // removed and new ones created/added to the ul element; but for now, it works
    }).children('li').unbind('mouseover.autoComplete').unbind('click.autoComplete')
    // New mouseover and click events
    .bind('mouseover.autoComplete', function() {
    $li = $(this);
    $('li.' + settings.rollover, $ul).removeClass(settings.rollover);
    $input.val($li.addClass(settings.rollover).data(settings.dataName).value);
    settings.onRollover.call($input[0], $li.data(settings.dataName), $li, $ul);
    settings.opt = $li.data('ac-index');
    }).bind('click.autoComplete', function() {
    settings.mouseClick = true;
    if (blurid) clearTimeout(blurid);
    settings.onSelect.call($input[0], $li.data(settings.dataName), $li, $ul);
    $ul.hide();
    // Bring the focus back to the input when clicking a list member
    $input.focus();
    });
    }
    });
    };
    })(jQuery);

Make it good use! :)

Thursday, November 12, 2009

Top 10 worst Microsoft products of all time

Just found this top, simple curiosity: http://www.v3.co.uk/v3/news/2252318/top-worst-microsoft-products

Wednesday, November 11, 2009

Execute CMD command on ASP.NET App

Hello guys, this time I spent one full day digging my way into this one..
The main question here is: How can I execute a command or a list of commands as if I where on the command prompt, and get the response?

I can give you right now the answer! (after banging my head over and over on the keyboard for one day...)

public static string ExecuteCmdCommands(List< string > commands)
        {
            Process p = new Process();
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.FileName = @"cmd";
            p.StartInfo.UseShellExecute = false;
            p.Start();

            StreamWriter sIn = p.StandardInput;
            sIn.AutoFlush = true;

            StreamReader sOut = p.StandardOutput;

            for (int i = 0; i <= commands.Count - 1; i++)
            {
                sIn.Write(commands[i] + System.Environment.NewLine);
            }
            
            sIn.Close();
            string result = sOut.ReadToEnd().Trim();
            p.Close();

            return result;
        }

How simple is it? You just create a Process, set the RedirectStandardOutput and RedirectStandardInput to true, so you can create a Stream for reading and writing to/from that process, the FileName is the prompt itself and the UseShellExecute is a pre requisite.
Then start writing out your instructions to the command prompt, always followed by a Environment.NewLine (simulate a Return) and afterwards, you get the output on the reader Stream, that simple!

And, you don't need to change any permissions on the user executing your worker process or app pool for your application, which is great so you don't create any security holes on your web server.

Hopefully this will save you hours, like it would if I had this before!

See you soon

Thursday, November 5, 2009

Colorize C# code with RegEx and HTML

Hi guys.
This time I needed to colorize some basic sample c# code and haven't found any simple and usable componente to do that, so I decided to code one, but very simple and basic.
I know it lacks some functionality, like coloring a class on a static method call, because this is not so simple as that, but it's a start, here is the code:
private string words = "abstract|as|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|volatile|void|while";
//this is a list of reserved words

private string ColorizeCode(string code)
{
  Regex regExInst = new Regex(@"(?\w+)\s\w+\s=\snew\s(?\w+)\(");//regex to colorize the object creations

  MatchCollection mcInst = regExInst.Matches(code);

  foreach (Match match in mcInst)
  {
    code = code.Remove(match.Groups["TYPE"].Index, match.Groups["TYPE"].Length);
    code = code.Insert(match.Groups["TYPE"].Index, "" + match.Groups["TYPE"].Value + "");

    Regex regExInstAux = new Regex(@"\s=\snew\s(?\w+)\(");
    MatchCollection mcInstAux = regExInstAux.Matches(code);
    foreach (Match matchAux in oMatchCollectionAux)
    {
      code = code.Remove(matchAux.Groups["TYPE"].Index,    matchAux.Groups["TYPE"].Length);
      code = code.Insert(matchAux.Groups["TYPE"].Index, "" + matchAux.Groups["TYPE"].Value + "");
    }
  }

  Regex regExCall = new Regex(@"(?\w+)\s\w+\s=\s\w+");
  //regex to colorize an object received from a method call

  MatchCollection mcCall = regExCall.Matches(code);

  foreach (Match match in mcCall)
  {
    code = code.Remove(match.Groups["TYPE"].Index, match.Groups["TYPE"].Length);
    code = code.Insert(match.Groups["TYPE"].Index, "" + match.Groups["TYPE"].Value + "");
  }

  string[] reservedWordArr = words.Split('|');

  for (int i = 0; i <= reservedWordArr.Length - 1; i++)
  {
    Regex regexRW = new Regex(@"\s(?" + reservedWordArr[i] + @")\s");
    //regex to colorize reserved words
    MatchCollection matchColRW = regexRW.Matches(code);
    foreach (Match match in matchColRW)
    {
      code = code.Remove(match.Groups["RW"].Index, match.Groups["RW"].Length);
      code = code.Insert(match.Groups["RW"].Index, "" + match.Groups["RW"].Value + "");
    }
  }

  return code;
}
and that's it! of course for your case, you change the markup I added..

See you soon :)

Monday, November 2, 2009

JQuery - ajax call with dynamic parameters

This one was discovered because a colleague of mine asked me, and I started to wonder and realized I never used it before, never really needed it..But indeed, it can be quite handy!
Before you make the $.ajax call or $.post call or whatever, you do this:

var parameters = {}; //declare the object
parameters["prop1"] = "value1"; //set some value
parameters["prop2"] = "value2"; //and again

Then, when you're making the actual request, you pass your object on the data field

$.ajax({
type: "POST",
url: "some.php",
data: parameters,
success: function(msg){
alert("nothing");
}
});

and voilá! you can build your parameters in a nice way, instead of "prop1=value1&prop2=value2"

Be back soon :)