var Pb = Pb ||
{};

Pb.Utils = Pb.Utils ||
{};

(function()
{
	if( window.jQuery )
	{
		( function( $ )
		{
			var TCookies = function() {
			    /**
			     * Private cookies array field
			     */
			    var cookies = [];
			    /**
			     * Private default options field
			     */
			    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) {
			            pair = separated[i].split('=');
			            name = pair[0].replace(/^\s*/, '').replace(/\s*$/, '');
			            value = decodeURIComponent(pair[1]);
			            cookies[name] = value;
			        }
			        return cookies;
			    };
			    
			    var Cookies = function () {
			    };
			    
			    Cookies.prototype = {
			        /**
			         * 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
			         */
			        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
			         */
			        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
			         */
			        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 + '=' + encodeURIComponent(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
			         */
			        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
			         */
			        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
			         */
			        setOptions: function(options) {
			            if (typeof options !== 'object') {
			                options = null;
			            }
			            
			            defaultOptions = resolveOptions(options);
			        }
			    };
			    
			    return {
					Cookies: new Cookies()
				};
			}();
			
			$.extend(Pb.Utils, TCookies);
			
			$.cookies = Pb.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 );
	}
})();