/**
* Copyright (c) 2005 - 2009, James Auldridge
* All rights reserved.
*
* Licensed under the BSD, MIT, and GPL (your choice!) Licenses:
*  http://code.google.com/p/cookies/wiki/License
*
*/
var jaaulde = window.jaaulde || {};
jaaulde.utils = jaaulde.utils || {};
jaaulde.utils.cookies = (function() {
    var cookies = [];

    var defaultOptions = {
        hoursToLive: null,
        path: '/',
        domain: null,
        secure: false
    };
    /**
    * resolveOptions - receive an options object and ensure all options are present and valid, replacing with defaults where necessary
    *
    * @access private
    * @static
    * @parameter Object options - optional options to start with
    * @return Object complete and valid options object
    */
    var resolveOptions = function(options) {
        var returnValue;

        if (typeof options !== 'object' || options === null) {
            returnValue = defaultOptions;
        }
        else {
            returnValue = {
                hoursToLive: (typeof options.hoursToLive === 'number' && options.hoursToLive !== 0 ? options.hoursToLive : defaultOptions.hoursToLive),
                path: (typeof options.path === 'string' && options.path !== '' ? options.path : defaultOptions.path),
                domain: (typeof options.domain === 'string' && options.domain !== '' ? options.domain : defaultOptions.domain),
                secure: (typeof options.secure === 'boolean' && options.secure ? options.secure : defaultOptions.secure)
            };
        }

        return returnValue;
    };
    /**
    * expiresGMTString - add given number of hours to current date/time and convert to GMT string
    *
    * @access private
    * @static
    * @parameter Integer hoursToLive - number of hours for which cookie should be valid
    * @return String - GMT time representing current date/time plus number of hours given
    */
    var expiresGMTString = function(hoursToLive) {
        var dateObject = new Date();
        dateObject.setTime(dateObject.getTime() + (hoursToLive * 60 * 60 * 1000));

        return dateObject.toGMTString();
    };
    /**
    * assembleOptionsString - analyze options and assemble appropriate string for setting a cookie with those options
    *
    * @access private
    * @static
    * @parameter Object options - optional options to start with
    * @return String - complete and valid cookie setting options
    */
    var assembleOptionsString = function(options) {
        options = resolveOptions(options);

        return (
			(typeof options.hoursToLive === 'number' ? '; expires=' + expiresGMTString(options.hoursToLive) : '') +
			'; path=' + options.path +
			(typeof options.domain === 'string' ? '; domain=' + options.domain : '') +
			(options.secure === true ? '; secure' : '')
		);
    };
    /**
    * splitCookies - retrieve document.cookie string and break it into a hash
    *
    * @access private
    * @static
    * @return Object - hash of cookies from document.cookie
    */
    var splitCookies = function() {
        cookies = {};
        var pair, name, value, separated = document.cookie.split(';');
        for (var i = 0; i < separated.length; i = i + 1) {
            value = "";
            pair = separated[i].split('=');
            name = pair[0].replace(/^\s*/, '').replace(/\s*$/, '');
            for (j = 1; j < pair.length; j++) {
                if (j < pair.length - 1)
                    value += decodeURIComponent(pair[j] + "=");
                else
                    value += decodeURIComponent(pair[j]);
            }
            cookies[name] = value;
        }
        return cookies;
    };

    var constructor = function() { };

    /**
    * get - get one, several, or all cookies
    *
    * @access public
    * @paramater Mixed cookieName - String:name of single cookie; Array:list of multiple cookie names; Void (no param):if you want all cookies
    * @return Mixed - String:if single cookie requested and found; Null:if single cookie requested and not found; Object:hash of multiple or all cookies
    */
    constructor.prototype.get = function(cookieName) {
        var returnValue;

        splitCookies();

        if (typeof cookieName === 'string') {
            returnValue = (typeof cookies[cookieName] !== 'undefined') ? cookies[cookieName] : null;
        }
        else if (typeof cookieName === 'object' && cookieName !== null) {
            returnValue = {};
            for (var item in cookieName) {
                if (typeof cookies[cookieName[item]] !== 'undefined') {
                    returnValue[cookieName[item]] = cookies[cookieName[item]];
                }
                else {
                    returnValue[cookieName[item]] = null;
                }
            }
        }
        else {
            returnValue = cookies;
        }

        return returnValue;
    };
    /**
    * filter - get array of cookies whose names match the provided RegExp
    *
    * @access public
    * @paramater Object RegExp - The regular expression to match against cookie names
    * @return Mixed - Object:hash of cookies whose names match the RegExp
    */
    constructor.prototype.filter = function(cookieNameRegExp) {
        var returnValue = {};

        splitCookies();

        if (typeof cookieNameRegExp === 'string') {
            cookieNameRegExp = new RegExp(cookieNameRegExp);
        }

        for (var cookieName in cookies) {
            if (cookieName.match(cookieNameRegExp)) {
                returnValue[cookieName] = cookies[cookieName];
            }
        }

        return returnValue;
    };
    /**
    * set - set or delete a cookie with desired options
    *
    * @access public
    * @paramater String cookieName - name of cookie to set
    * @paramater Mixed value - Null:if deleting, String:value to assign cookie if setting
    * @paramater Object options - optional list of cookie options to specify (hoursToLive, path, domain, secure)
    * @return void
    */
    constructor.prototype.set = function(cookieName, value, options) {
        if (typeof value === 'undefined' || value === null) {
            if (typeof options !== 'object' || options === null) {
                options = {};
            }
            value = '';
            options.hoursToLive = -8760;
        }

        var optionsString = assembleOptionsString(options);

        document.cookie = cookieName + '=' + value + optionsString;
    };
    /**
    * del - delete a cookie (domain and path options must match those with which the cookie was set; this is really an alias for set() with parameters simplified for this use)
    *
    * @access public
    * @paramater MIxed cookieName - String name of cookie to delete, or Bool true to delete all
    * @paramater Object options - optional list of cookie options to specify ( path, domain )
    * @return void
    */
    constructor.prototype.del = function(cookieName, options) {
        var allCookies = {};

        if (typeof options !== 'object' || options === null) {
            options = {};
        }

        if (typeof cookieName === 'boolean' && cookieName === true) {
            allCookies = this.get();
        }
        else if (typeof cookieName === 'string') {
            allCookies[cookieName] = true;
        }

        for (var name in allCookies) {
            if (typeof name === 'string' && name !== '') {
                this.set(name, null, options);
            }
        }
    };
    /**
    * test - test whether the browser is accepting cookies
    *
    * @access public
    * @return Boolean
    */
    constructor.prototype.test = function() {
        var returnValue = false, testName = 'cT', testValue = 'data';

        this.set(testName, testValue);

        if (this.get(testName) === testValue) {
            this.del(testName);
            returnValue = true;
        }

        return returnValue;
    };
    /**
    * setOptions - set default options for calls to cookie methods
    *
    * @access public
    * @param Object options - list of cookie options to specify (hoursToLive, path, domain, secure)
    * @return void
    */
    constructor.prototype.setOptions = function(options) {
        if (typeof options !== 'object') {
            options = null;
        }

        defaultOptions = resolveOptions(options);
    };

    return new constructor();
})();

(function() {
    if (window.jQuery) {
        (function($) {
            $.cookies = jaaulde.utils.cookies;

            var extensions = {
                /**
                * $( 'selector' ).cookify - set the value of an input field or the innerHTML of an element to a cookie by the name or id of the field or element
                *                           (radio and checkbox not yet supported)
                *                           (field or element MUST have name or id attribute)
                *
                * @access public
                * @param Object options - list of cookie options to specify
                * @return Object jQuery
                */
                cookify: function(options) {
                    return this.each(function() {
                        var i, resolvedName = false, resolvedValue = false, name = '', value = '', nameAttrs = ['name', 'id'], nodeName, inputType;

                        for (i in nameAttrs) {
                            if (!isNaN(i)) {
                                name = $(this).attr(nameAttrs[i]);
                                if (typeof name === 'string' && name !== '') {
                                    resolvedName = true;
                                    break;
                                }
                            }
                        }

                        if (resolvedName) {
                            nodeName = this.nodeName.toLowerCase();
                            if (nodeName !== 'input' && nodeName !== 'textarea' && nodeName !== 'select' && nodeName !== 'img') {
                                value = $(this).html();
                                resolvedValue = true;
                            }
                            else {
                                inputType = $(this).attr('type');
                                if (typeof inputType === 'string' && inputType !== '') {
                                    inputType = inputType.toLowerCase();
                                }
                                if (inputType !== 'radio' && inputType !== 'checkbox') {
                                    value = $(this).val();
                                    resolvedValue = true;
                                }
                            }

                            if (resolvedValue) {
                                if (typeof value !== 'string' || value === '') {
                                    value = null;
                                }
                                $.cookies.set(name, value, options);
                            }
                        }
                    });
                },
                /**
                * $( 'selector' ).cookieFill - set the value of an input field or the innerHTML of an element from a cookie by the name or id of the field or element
                *
                * @access public
                * @return Object jQuery
                */
                cookieFill: function() {
                    return this.each(function() {
                        var i, resolvedName = false, name = '', value, nameAttrs = ['name', 'id'], iteration = 0, nodeName;

                        for (i in nameAttrs) {
                            if (!isNaN(i)) {
                                name = $(this).attr(nameAttrs[i]);
                                if (typeof name === 'string' && name !== '') {
                                    resolvedName = true;
                                    break;
                                }
                            }
                        }

                        if (resolvedName) {
                            value = $.cookies.get(name);
                            if (value !== null) {
                                nodeName = this.nodeName.toLowerCase();
                                if (nodeName === 'input' || nodeName === 'textarea' || nodeName === 'select') {
                                    $(this).val(value);
                                }
                                else {
                                    $(this).html(value);
                                }
                            }
                        }

                        iteration = 0;
                    });
                },
                /**
                * $( 'selector' ).cookieBind - call cookie fill on matching elements, and bind their change events to cookify()
                *
                * @access public
                * @param Object options - list of cookie options to specify
                * @return Object jQuery
                */
                cookieBind: function(options) {
                    return this.each(function() {
                        $(this).cookieFill().change(function() {
                            $(this).cookify(options);
                        });
                    });
                }
            };

            $.each(extensions, function(i) {
                $.fn[i] = this;
            });

        })(window.jQuery);
    }
})();
