(function($) {
    
    /*
    * Syntax:
    * $.create(element[, attributes[, children]])
    */
    
     // register jQuery extension
    $.extend({
        create: function(element, attributes, children) {
        
            // create new element
            var elem = $(document.createElement(element));
            
            // add passed attributes
            if (typeof(attributes) == 'object') {
                for (key in attributes) {
                    elem.attr(key, attributes[key]);
                }
            }
            
            // add passed child elements
            if (typeof(children) == 'string') {
                elem.text(children);
            } else if (typeof(children) == 'object') {
                for (i = 0; i < children.length; i++) {
                    elem.append(children[i]);
                }
            }
            return elem;
        }
    });

})(jQuery);

(function($) {
    
    /*
    * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
    * @desc Create a cookie with all available options.
    */
    // register jQuery extension
    $.extend({
        cookie: function(name, value, options) {
            if (typeof value != 'undefined') { // name and value given, set cookie
                options = options || {};
                if (value === null) {
                    value = '';
                    options.expires = -1;
                }
                var expires = '';
                if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
                    var date;
                    if (typeof options.expires == 'number') {
                        date = new Date();
                        date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
                    } else {
                        date = options.expires;
                    }
                    expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
                }
                // CAUTION: Needed to parenthesize options.path and options.domain
                // in the following expressions, otherwise they evaluate to undefined
                // in the packed version for some reason...
                var path = options.path ? '; path=' + (options.path) : '';
                var domain = options.domain ? '; domain=' + (options.domain) : '';
                var secure = options.secure ? '; secure' : '';
                document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
            } else { // only name given, get cookie
                var cookieValue = null;
                if (document.cookie && document.cookie != '') {
                    var cookies = document.cookie.split(';');
                    for (var i = 0; i < cookies.length; i++) {
                        var cookie = jQuery.trim(cookies[i]);
                        // Does this cookie string begin with the name we want?
                        if (cookie.substring(0, name.length + 1) == (name + '=')) {
                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                            break;
                        }
                    }
                }
                return cookieValue;
            }
        }
    });

})(jQuery);

(function($) {   
    function toIntegersAtLease(n) 
    // Format integers to have at least two digits.
    {    
        return n < 10 ? '0' + n : n;
    }

    Date.prototype.toJSON = function(date)
    // Yes, it polutes the Date namespace, but we'll allow it here, as
    // it's damned usefull.
    {
        return this.getUTCFullYear()   + '-' +
             toIntegersAtLease(this.getUTCMonth()) + '-' +
             toIntegersAtLease(this.getUTCDate());
    };

    var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
    var meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };
        
    $.quoteString = function(string)
    // Places quotes around a string, inteligently.
    // If the string contains no control characters, no quote characters, and no
    // backslash characters, then we can safely slap some quotes around it.
    // Otherwise we must also replace the offending characters with safe escape
    // sequences.
    {
        if (escapeable.test(string))
        {
            return '"' + string.replace(escapeable, function (a) 
            {
                var c = meta[a];
                if (typeof c === 'string') {
                    return c;
                }
                c = a.charCodeAt();
                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
            }) + '"';
        }
        return '"' + string + '"';
    };
    
    $.toJSON = function(o, compact)
    {
        var type = typeof(o);
        
        if (type == "undefined")
            return "undefined";
        else if (type == "number" || type == "boolean")
            return o + "";
        else if (o === null)
            return "null";
        
        // Is it a string?
        if (type == "string") 
        {
            return $.quoteString(o);
        }
        
        // Does it have a .toJSON function?
        if (type == "object" && typeof o.toJSON == "function") 
            return o.toJSON(compact);
        
        // Is it an array?
        if (type != "function" && typeof(o.length) == "number") 
        {
            var ret = [];
            for (var i = 0; i < o.length; i++) {
                ret.push( $.toJSON(o[i], compact) );
            }
            if (compact)
                return "[" + ret.join(",") + "]";
            else
                return "[" + ret.join(", ") + "]";
        }
        
        // If it's a function, we have to warn somebody!
        if (type == "function") {
            throw new TypeError("Unable to convert object of type 'function' to json.");
        }
        
        // It's probably an object, then.
        var ret = [];
        for (var k in o) {
            var name;
            type = typeof(k);
            
            if (type == "number")
                name = '"' + k + '"';
            else if (type == "string")
                name = $.quoteString(k);
            else
                continue;  //skip non-string or number keys
            
            var val = $.toJSON(o[k], compact);
            if (typeof(val) != "string") {
                // skip non-serializable values
                continue;
            }
            
            if (compact)
                ret.push(name + ":" + val);
            else
                ret.push(name + ": " + val);
        }
        return "{" + ret.join(", ") + "}";
    };
    
    $.compactJSON = function(o)
    {
        return $.toJSON(o, true);
    };
    
    $.evalJSON = function(src)
    // Evals JSON that we know to be safe.
    {
        return eval("(" + src + ")");
    };
    
    $.secureEvalJSON = function(src)
    // Evals JSON in a way that is *more* secure.
    {
        var filtered = src;
        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
        
        if (/^[\],:{}\s]*$/.test(filtered))
            return eval("(" + src + ")");
        else
            throw new SyntaxError("Error parsing JSON, source is not valid.");
    };
})(jQuery);

var initialized = false;
$.extend($.ui.tabs.prototype, {
        paging: function(options) {
                var opts = {
                        tabsPerPage: 0,
                        nextButton: '&#187;',
                        prevButton: '&#171;',
                        follow: false,
                        cycle: false
                };
               
                opts = jQuery.extend(opts, options);

                var self = this, initialized = false, currentPage,
                        buttonWidth, containerWidth, allTabsWidth, tabWidths,
                        maxPageWidth, pages, resizeTimer = null,
                        windowHeight = $(window).height(), windowWidth = $(window).width();
                               
                function init() {
                        allTabsWidth = 0, currentPage = 0, maxPageWidth = 0, buttonWidth = 0,
                                pages = new Array(), tabWidths = new Array(), selectedTabWidths = new Array();
                       
                        containerWidth = $(self.element).width();
                       
                        // create next button                  
                        $li = $('<li></li>')
                                .addClass('ui-tabs-paging-next');
                        $a = $('<a href="#"></a>')
                                .click(function() { page('next'); return false; })
                                .html(opts.nextButton);
                        $li.append($a);
                       
                        self.lis.eq(self.length()-1).after($li);
                        buttonWidth = $li.outerWidth({ margin: true }) + 30;
                       
                        // create prev button
                        $li = $('<li></li>')
                                .addClass('ui-tabs-paging-prev');
                        $a = $('<a href="#"></a>')
                                .click(function() { page('prev'); return false; })
                                .html(opts.prevButton);
                        $li.append($a);
                        self.lis.eq(0).before($li);
                        buttonWidth += $li.outerWidth({ margin: true });
                       
                        // loops through LIs, get width of each tab when selected and unselected.
                        self.lis.each(function(i) {                    
                                if (i == self.options.selected) {
                                        selectedTabWidths[i] = $(this).outerWidth({ margin: true });
                                        tabWidths[i] = self.lis.eq(i).removeClass('ui-tabs-selected').outerWidth({ margin: true });
                                        self.lis.eq(i).addClass('ui-tabs-selected');
                                        allTabsWidth += selectedTabWidths[i];                                  
                                } else {
                                        tabWidths[i] = $(this).outerWidth({ margin: true });
                                        selectedTabWidths[i] = self.lis.eq(i).addClass('ui-tabs-selected').outerWidth({ margin: true });
                                        self.lis.eq(i).removeClass('ui-tabs-selected');
                                        allTabsWidth += tabWidths[i];
                                }
                        });
                       
                        // if the width of all tables is greater than the container's width, calculate the pages
                        if (allTabsWidth > containerWidth) {
                                var pageIndex = 0, pageWidth = 0, maxTabPadding = 0;
                               
                                // start calculating pageWidths
                                for (var i = 0; i < tabWidths.length; i++) {
                                        // if first tab of page or selected tab's padding larger than the current max, set the maxTabPadding
                                        if (pageWidth == 0 || selectedTabWidths[i] - tabWidths[i] > maxTabPadding)
                                                maxTabPadding = (selectedTabWidths[i] - tabWidths[i]);
                                       
                                        // if first tab of page, initialize pages variable for page
                                        if (pages[pageIndex] == null) {
                                                pages[pageIndex] = { start: i };
                                       
                                        } else if ((i > 0 && (i % opts.tabsPerPage) == 0) || (tabWidths[i] + pageWidth + buttonWidth + 12) > containerWidth) {
                                                if ((pageWidth + maxTabPadding) > maxPageWidth)
                                                        maxPageWidth = (pageWidth + maxTabPadding);
                                                pageIndex++;
                                                pages[pageIndex] = { start: i };                        
                                                pageWidth = 0;
                                        }
                                        pages[pageIndex].end = i+1;
                                        pageWidth += tabWidths[i];
                                        if (i == self.options.selected) currentPage = pageIndex;
                                }
                                if ((pageWidth + maxTabPadding) > maxPageWidth)
                                        maxPageWidth = (pageWidth + maxTabPadding);                            

                            // hide all tabs then show tabs for current page
                                self.lis.hide().slice(pages[currentPage].start, pages[currentPage].end).show();
                                if (currentPage == (pages.length - 1) && !opts.cycle)
                                        disableButton('next');                  
                                if (currentPage == 0 && !opts.cycle)
                                        disableButton('prev');
                               
                                // calculate the right padding for the next button
                                buttonPadding = containerWidth - maxPageWidth - buttonWidth - ($.browser.msie?8:0);
                                if (buttonPadding > 0)
                                        $('.ui-tabs-paging-next', self.element).css({ paddingRight: buttonPadding + 'px' });
                               
                                initialized = true;
                        } else {
                                destroy();
                        }
                       
                        $(window).bind('resize', handleResize);
                }
               
                function page(direction) {                                      
                        currentPage = currentPage + (direction == 'prev'?-1:1);
                       
                        if (direction == 'prev' && currentPage < 0 && opts.cycle)
                                currentPage = pages.length - 1;
                        else if ((direction == 'prev' && currentPage < 0) ||
                                 (direction == 'next' && currentPage >= pages.length))
                                currentPage = 0;
                       
                        var start = pages[currentPage].start;
                        var end = pages[currentPage].end;
                        self.lis.hide().slice(pages[currentPage].start, pages[currentPage].end).show();
                       
                        if (direction == 'prev') {
                                enableButton('next');
                                if (opts.follow && (self.options.selected < start || self.options.selected > (end-1))) self.select(end-1);
                                if (!opts.cycle && start <= 0) disableButton('prev');
                        } else {
                                enableButton('prev');
                                if (opts.follow && (self.options.selected < start || self.options.selected > (end-1))) self.select(start);
                                if (!opts.cycle && end >= self.length()) disableButton('next');
                        }
                }
               
                function disableButton(direction) {
                        $('.ui-tabs-paging-'+direction, self.element).addClass('ui-tabs-paging-disabled');
                }
               
                function enableButton(direction) {
                        $('.ui-tabs-paging-'+direction, self.element).removeClass('ui-tabs-paging-disabled');
                }
               
                // special function defined to handle IE6 and IE7 resize issues
                function handleResize() {
                        if (resizeTimer) clearTimeout(resizeTimer);
                       
                        if (windowHeight != $(window).height() || windowWidth != $(window).width())
                                resizeTimer = setTimeout(reinit, 100);
                }
               
                function reinit() {    
                        windowHeight = $(window).height();
                        windowWidth = $(window).width();
                        destroy();
                        init();
                }
               
                function destroy() {    
                        // remove buttons
                        $('.ui-tabs-paging-next', self.element).remove();
                        $('.ui-tabs-paging-prev', self.element).remove();
                       
                        // show all tabs
                        self.lis.show();
                       
                        initialized = false;

                        $(window).unbind('resize', handleResize);
                }
               
                init();
               
                // add, remove, and destroy functions specific for paging
                $.extend($.ui.tabs.prototype, {
                        pagingAdd: function(url, label, index) {
                                if (initialized) {
                                        destroy();
                                        this.add(url, label, index);
                                        init();
                                } else {
                                        this.add(url, label, index);
                                }
                        },
                        pagingRemove: function(index) {
                                if (initialized) {
                                        destroy();
                                        this.remove(index);
                                        init();
                                } else
                                        this.remove(index);
                        },
                        pagingDestroy: function() {
                                destroy();
                        }
                });


        }
});
