﻿

// START PLUGINS

/**
* jCarousel - Riding carousels with jQuery
*   http://sorgalla.com/jcarousel/
*
* Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* Built on top of the jQuery library
*   http://jquery.com
*
* Inspired by the "Carousel Component" by Bill Scott
*   http://billwscott.com/carousel/
*/

(function($) {
    /**
    * Creates a carousel for all matched elements.
    *
    * @example $("#mycarousel").jcarousel();
    * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul>
    * @result
    *
    * <div class="jcarousel-skin-name">
    *   <div class="jcarousel-container">
    *     <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div>
    *     <div class="jcarousel-next"></div>
    *     <div class="jcarousel-clip">
    *       <ul class="jcarousel-list">
    *         <li class="jcarousel-item-1">First item</li>
    *         <li class="jcarousel-item-2">Second item</li>
    *       </ul>
    *     </div>
    *   </div>
    * </div>
    *
    * @name jcarousel
    * @type jQuery
    * @param Hash o A set of key/value pairs to set as configuration properties.
    * @cat Plugins/jCarousel
    */
    $.fn.jcarousel = function(o) {
        return this.each(function() {
            new $jc(this, o);
        });
    };

    // Default configuration properties.
    var defaults = {
        vertical: false,
        start: 1,
        offset: 1,
        size: null,
        scroll: 3,
        visible: null,
        animation: 'normal',
        easing: 'swing',
        auto: 0,
        wrap: null,
        initCallback: null,
        reloadCallback: null,
        itemLoadCallback: null,
        itemFirstInCallback: null,
        itemFirstOutCallback: null,
        itemLastInCallback: null,
        itemLastOutCallback: null,
        itemVisibleInCallback: null,
        itemVisibleOutCallback: null,
        buttonNextHTML: '<div></div>',
        buttonPrevHTML: '<div></div>',
        buttonNextEvent: 'click',
        buttonPrevEvent: 'click',
        buttonNextCallback: null,
        buttonPrevCallback: null
    };

    /**
    * The jCarousel object.
    *
    * @constructor
    * @name $.jcarousel
    * @param Object e The element to create the carousel for.
    * @param Hash o A set of key/value pairs to set as configuration properties.
    * @cat Plugins/jCarousel
    */
    $.jcarousel = function(e, o) {
        this.options = $.extend({}, defaults, o || {});

        this.locked = false;

        this.container = null;
        this.clip = null;
        this.list = null;
        this.buttonNext = null;
        this.buttonPrev = null;

        this.wh = !this.options.vertical ? 'width' : 'height';
        this.lt = !this.options.vertical ? 'left' : 'top';

        // Extract skin class
        var skin = '', split = e.className.split(' ');

        for (var i = 0; i < split.length; i++) {
            if (split[i].indexOf('jcarousel-skin') != -1) {
                $(e).removeClass(split[i]);
                var skin = split[i];
                break;
            }
        }

        if (e.nodeName == 'UL' || e.nodeName == 'OL') {
            this.list = $(e);
            this.container = this.list.parent();

            if (this.container.hasClass('jcarousel-clip')) {
                if (!this.container.parent().hasClass('jcarousel-container'))
                    this.container = this.container.wrap('<div></div>');

                this.container = this.container.parent();
            } else if (!this.container.hasClass('jcarousel-container'))
                this.container = this.list.wrap('<div></div>').parent();
        } else {
            this.container = $(e);
            this.list = $(e).find('>ul,>ol,div>ul,div>ol');
        }

        if (skin != '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1)
            this.container.wrap('<div class=" ' + skin + '"></div>');

        this.clip = this.list.parent();

        if (!this.clip.length || !this.clip.hasClass('jcarousel-clip'))
            this.clip = this.list.wrap('<div></div>').parent();

        this.buttonPrev = $('.jcarousel-prev', this.container);

        if (this.buttonPrev.size() == 0 && this.options.buttonPrevHTML != null)
            this.buttonPrev = this.clip.before(this.options.buttonPrevHTML).prev();

        this.buttonPrev.addClass(this.className('jcarousel-prev'));

        this.buttonNext = $('.jcarousel-next', this.container);

        if (this.buttonNext.size() == 0 && this.options.buttonNextHTML != null)
            this.buttonNext = this.clip.before(this.options.buttonNextHTML).prev();

        this.buttonNext.addClass(this.className('jcarousel-next'));

        this.clip.addClass(this.className('jcarousel-clip'));
        this.list.addClass(this.className('jcarousel-list'));
        this.container.addClass(this.className('jcarousel-container'));

        var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
        var li = this.list.children('li');

        var self = this;

        if (li.size() > 0) {
            var wh = 0, i = this.options.offset;
            li.each(function() {
                self.format(this, i++);
                wh += self.dimension(this, di);
            });

            this.list.css(this.wh, wh + 'px');

            // Only set if not explicitly passed as option
            if (!o || o.size === undefined)
                this.options.size = li.size();
        }

        // For whatever reason, .show() does not work in Safari...
        this.container.css('display', 'block');
        this.buttonNext.css('display', 'block');
        this.buttonPrev.css('display', 'block');

        this.funcNext = function() { self.next(); };
        this.funcPrev = function() { self.prev(); };
        this.funcResize = function() { self.reload(); };

        if (this.options.initCallback != null)
            this.options.initCallback(this, 'init');

        if ($.browser.safari) {
            this.buttons(false, false);
            $(window).bind('load', function() { self.setup(); });
        } else
            this.setup();
    };

    // Create shortcut for internal use
    var $jc = $.jcarousel;

    $jc.fn = $jc.prototype = {
        jcarousel: '0.2.3'
    };

    $jc.fn.extend = $jc.extend = $.extend;

    $jc.fn.extend({
        /**
        * Setups the carousel.
        *
        * @name setup
        * @type undefined
        * @cat Plugins/jCarousel
        */
        setup: function() {
            this.first = null;
            this.last = null;
            this.prevFirst = null;
            this.prevLast = null;
            this.animating = false;
            this.timer = null;
            this.tail = null;
            this.inTail = false;

            if (this.locked)
                return;

            this.list.css(this.lt, this.pos(this.options.offset) + 'px');
            var p = this.pos(this.options.start);
            this.prevFirst = this.prevLast = null;
            this.animate(p, false);

            $(window).unbind('resize', this.funcResize).bind('resize', this.funcResize);
        },

        /**
        * Clears the list and resets the carousel.
        *
        * @name reset
        * @type undefined
        * @cat Plugins/jCarousel
        */
        reset: function() {
            this.list.empty();

            this.list.css(this.lt, '0px');
            this.list.css(this.wh, '10px');

            if (this.options.initCallback != null)
                this.options.initCallback(this, 'reset');

            this.setup();
        },

        /**
        * Reloads the carousel and adjusts positions.
        *
        * @name reload
        * @type undefined
        * @cat Plugins/jCarousel
        */
        reload: function() {
            if (this.tail != null && this.inTail)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail);

            this.tail = null;
            this.inTail = false;

            if (this.options.reloadCallback != null)
                this.options.reloadCallback(this);

            if (this.options.visible != null) {
                var self = this;
                var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0;
                $('li', this.list).each(function(i) {
                    wh += self.dimension(this, di);
                    if (i + 1 < self.first)
                        lt = wh;
                });

                this.list.css(this.wh, wh + 'px');
                this.list.css(this.lt, -lt + 'px');
            }

            this.scroll(this.first, false);
        },

        /**
        * Locks the carousel.
        *
        * @name lock
        * @type undefined
        * @cat Plugins/jCarousel
        */
        lock: function() {
            this.locked = true;
            this.buttons();
        },

        /**
        * Unlocks the carousel.
        *
        * @name unlock
        * @type undefined
        * @cat Plugins/jCarousel
        */
        unlock: function() {
            this.locked = false;
            this.buttons();
        },

        /**
        * Sets the size of the carousel.
        *
        * @name size
        * @type undefined
        * @param Number s The size of the carousel.
        * @cat Plugins/jCarousel
        */
        size: function(s) {
            if (s != undefined) {
                this.options.size = s;
                if (!this.locked)
                    this.buttons();
            }

            return this.options.size;
        },

        /**
        * Checks whether a list element exists for the given index (or index range).
        *
        * @name get
        * @type bool
        * @param Number i The index of the (first) element.
        * @param Number i2 The index of the last element.
        * @cat Plugins/jCarousel
        */
        has: function(i, i2) {
            if (i2 == undefined || !i2)
                i2 = i;

            if (this.options.size !== null && i2 > this.options.size)
                i2 = this.options.size;

            for (var j = i; j <= i2; j++) {
                var e = this.get(j);
                if (!e.length || e.hasClass('jcarousel-item-placeholder'))
                    return false;
            }

            return true;
        },

        /**
        * Returns a jQuery object with list element for the given index.
        *
        * @name get
        * @type jQuery
        * @param Number i The index of the element.
        * @cat Plugins/jCarousel
        */
        get: function(i) {
            return $('.jcarousel-item-' + i, this.list);
        },

        /**
        * Adds an element for the given index to the list.
        * If the element already exists, it updates the inner html.
        * Returns the created element as jQuery object.
        *
        * @name add
        * @type jQuery
        * @param Number i The index of the element.
        * @param String s The innerHTML of the element.
        * @cat Plugins/jCarousel
        */
        add: function(i, s) {
            var e = this.get(i), old = 0, add = 0;

            if (e.length == 0) {
                var c, e = this.create(i), j = $jc.intval(i);
                while (c = this.get(--j)) {
                    if (j <= 0 || c.length) {
                        j <= 0 ? this.list.prepend(e) : c.after(e);
                        break;
                    }
                }
            } else
                old = this.dimension(e);

            e.removeClass(this.className('jcarousel-item-placeholder'));
            typeof s == 'string' ? e.html(s) : e.empty().append(s);

            var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
            var wh = this.dimension(e, di) - old;

            if (i > 0 && i < this.first)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px');

            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px');

            return e;
        },

        /**
        * Removes an element for the given index from the list.
        *
        * @name remove
        * @type undefined
        * @param Number i The index of the element.
        * @cat Plugins/jCarousel
        */
        remove: function(i) {
            var e = this.get(i);

            // Check if item exists and is not currently visible
            if (!e.length || (i >= this.first && i <= this.last))
                return;

            var d = this.dimension(e);

            if (i < this.first)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px');

            e.remove();

            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px');
        },

        /**
        * Moves the carousel forwards.
        *
        * @name next
        * @type undefined
        * @cat Plugins/jCarousel
        */
        next: function() {
            this.stopAuto();

            if (this.tail != null && !this.inTail)
                this.scrollTail(false);
            else
                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last') && this.options.size != null && this.last == this.options.size) ? 1 : this.first + this.options.scroll);
        },

        /**
        * Moves the carousel backwards.
        *
        * @name prev
        * @type undefined
        * @cat Plugins/jCarousel
        */
        prev: function() {
            this.stopAuto();

            if (this.tail != null && this.inTail)
                this.scrollTail(true);
            else
                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first') && this.options.size != null && this.first == 1) ? this.options.size : this.first - this.options.scroll);
        },

        /**
        * Scrolls the tail of the carousel.
        *
        * @name scrollTail
        * @type undefined
        * @param Bool b Whether scroll the tail back or forward.
        * @cat Plugins/jCarousel
        */
        scrollTail: function(b) {
            if (this.locked || this.animating || !this.tail)
                return;

            var pos = $jc.intval(this.list.css(this.lt));

            !b ? pos -= this.tail : pos += this.tail;
            this.inTail = !b;

            // Save for callbacks
            this.prevFirst = this.first;
            this.prevLast = this.last;

            this.animate(pos);
        },

        /**
        * Scrolls the carousel to a certain position.
        *
        * @name scroll
        * @type undefined
        * @param Number i The index of the element to scoll to.
        * @param Bool a Flag indicating whether to perform animation.
        * @cat Plugins/jCarousel
        */
        scroll: function(i, a) {
            if (this.locked || this.animating)
                return;

            this.animate(this.pos(i), a);
        },

        /**
        * Prepares the carousel and return the position for a certian index.
        *
        * @name pos
        * @type Number
        * @param Number i The index of the element to scoll to.
        * @cat Plugins/jCarousel
        */
        pos: function(i) {
            if (this.locked || this.animating)
                return;

            if (this.options.wrap != 'circular')
                i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i);

            var back = this.first > i;
            var pos = $jc.intval(this.list.css(this.lt));

            // Create placeholders, new list width/height
            // and new list position
            var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
            var c = back ? this.get(f) : this.get(this.last);
            var j = back ? f : f - 1;
            var e = null, l = 0, p = false, d = 0;

            while (back ? --j >= i : ++j < i) {
                e = this.get(j);
                p = !e.length;
                if (e.length == 0) {
                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
                    c[back ? 'before' : 'after'](e);
                }

                c = e;
                d = this.dimension(e);

                if (p)
                    l += d;

                if (this.first != null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size == null || j <= this.options.size))))
                    pos = back ? pos + d : pos - d;
            }

            // Calculate visible items
            var clipping = this.clipping();
            var cache = [];
            var visible = 0, j = i, v = 0;
            var c = this.get(i - 1);

            while (++visible) {
                e = this.get(j);
                p = !e.length;
                if (e.length == 0) {
                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
                    // This should only happen on a next scroll
                    c.length == 0 ? this.list.prepend(e) : c[back ? 'before' : 'after'](e);
                }

                c = e;
                var d = this.dimension(e);
                if (d == 0) {
                    alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
                    return 0;
                }

                if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size)
                    cache.push(e);
                else if (p)
                    l += d;

                v += d;

                if (v >= clipping)
                    break;

                j++;
            }

            // Remove out-of-range placeholders
            for (var x = 0; x < cache.length; x++)
                cache[x].remove();

            // Resize list
            if (l > 0) {
                this.list.css(this.wh, this.dimension(this.list) + l + 'px');

                if (back) {
                    pos -= l;
                    this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px');
                }
            }

            // Calculate first and last item
            var last = i + visible - 1;
            if (this.options.wrap != 'circular' && this.options.size && last > this.options.size)
                last = this.options.size;

            if (j > last) {
                visible = 0, j = last, v = 0;
                while (++visible) {
                    var e = this.get(j--);
                    if (!e.length)
                        break;
                    v += this.dimension(e);
                    if (v >= clipping)
                        break;
                }
            }

            var first = last - visible + 1;
            if (this.options.wrap != 'circular' && first < 1)
                first = 1;

            if (this.inTail && back) {
                pos += this.tail;
                this.inTail = false;
            }

            this.tail = null;
            if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) {
                var m = $jc.margin(this.get(last), !this.options.vertical ? 'marginRight' : 'marginBottom');
                if ((v - m) > clipping)
                    this.tail = v - clipping - m;
            }

            // Adjust position
            while (i-- > first)
                pos += this.dimension(this.get(i));

            // Save visible item range
            this.prevFirst = this.first;
            this.prevLast = this.last;
            this.first = first;
            this.last = last;

            return pos;
        },

        /**
        * Animates the carousel to a certain position.
        *
        * @name animate
        * @type undefined
        * @param mixed p Position to scroll to.
        * @param Bool a Flag indicating whether to perform animation.
        * @cat Plugins/jCarousel
        */
        animate: function(p, a) {
            if (this.locked || this.animating)
                return;

            this.animating = true;

            var self = this;
            var scrolled = function() {
                self.animating = false;

                if (p == 0)
                    self.list.css(self.lt, 0);

                if (self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size == null || self.last < self.options.size)
                    self.startAuto();

                self.buttons();
                self.notify('onAfterAnimation');
            };

            this.notify('onBeforeAnimation');

            // Animate
            if (!this.options.animation || a == false) {
                this.list.css(this.lt, p + 'px');
                scrolled();
            } else {
                var o = !this.options.vertical ? { 'left': p} : { 'top': p };
                this.list.animate(o, this.options.animation, this.options.easing, scrolled);
            }
        },

        /**
        * Starts autoscrolling.
        *
        * @name auto
        * @type undefined
        * @param Number s Seconds to periodically autoscroll the content.
        * @cat Plugins/jCarousel
        */
        startAuto: function(s) {
            if (s != undefined)
                this.options.auto = s;

            if (this.options.auto == 0)
                return this.stopAuto();

            if (this.timer != null)
                return;

            var self = this;
            this.timer = setTimeout(function() { self.next(); }, this.options.auto * 1000);
        },

        /**
        * Stops autoscrolling.
        *
        * @name stopAuto
        * @type undefined
        * @cat Plugins/jCarousel
        */
        stopAuto: function() {
            if (this.timer == null)
                return;

            clearTimeout(this.timer);
            this.timer = null;
        },

        /**
        * Sets the states of the prev/next buttons.
        *
        * @name buttons
        * @type undefined
        * @cat Plugins/jCarousel
        */
        buttons: function(n, p) {
            if (n == undefined || n == null) {
                var n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size == null || this.last < this.options.size);
                if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size != null && this.last >= this.options.size)
                    n = this.tail != null && !this.inTail;
            }

            if (p == undefined || p == null) {
                var p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
                if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size != null && this.first == 1)
                    p = this.tail != null && this.inTail;
            }

            var self = this;

            this.buttonNext[n ? 'bind' : 'unbind'](this.options.buttonNextEvent, this.funcNext)[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
            this.buttonPrev[p ? 'bind' : 'unbind'](this.options.buttonPrevEvent, this.funcPrev)[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true);

            if (this.buttonNext.length > 0 && (this.buttonNext[0].jcarouselstate == undefined || this.buttonNext[0].jcarouselstate != n) && this.options.buttonNextCallback != null) {
                this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); });
                this.buttonNext[0].jcarouselstate = n;
            }

            if (this.buttonPrev.length > 0 && (this.buttonPrev[0].jcarouselstate == undefined || this.buttonPrev[0].jcarouselstate != p) && this.options.buttonPrevCallback != null) {
                this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); });
                this.buttonPrev[0].jcarouselstate = p;
            }
        },

        notify: function(evt) {
            var state = this.prevFirst == null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');

            // Load items
            this.callback('itemLoadCallback', evt, state);

            if (this.prevFirst !== this.first) {
                this.callback('itemFirstInCallback', evt, state, this.first);
                this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
            }

            if (this.prevLast !== this.last) {
                this.callback('itemLastInCallback', evt, state, this.last);
                this.callback('itemLastOutCallback', evt, state, this.prevLast);
            }

            this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
            this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
        },

        callback: function(cb, evt, state, i1, i2, i3, i4) {
            if (this.options[cb] == undefined || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation'))
                return;

            var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];

            if (!$.isFunction(callback))
                return;

            var self = this;

            if (i1 === undefined)
                callback(self, state, evt);
            else if (i2 === undefined)
                this.get(i1).each(function() { callback(self, this, i1, state, evt); });
            else {
                for (var i = i1; i <= i2; i++)
                    if (i !== null && !(i >= i3 && i <= i4))
                    this.get(i).each(function() { callback(self, this, i, state, evt); });
            }
        },

        create: function(i) {
            return this.format('<li></li>', i);
        },

        format: function(e, i) {
            var $e = $(e).addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i));
            $e.attr('jcarouselindex', i);
            return $e;
        },

        className: function(c) {
            return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical');
        },

        dimension: function(e, d) {
            var el = e.jquery != undefined ? e[0] : e;

            var old = !this.options.vertical ?
                el.offsetWidth + $jc.margin(el, 'marginLeft') + $jc.margin(el, 'marginRight') :
                el.offsetHeight + $jc.margin(el, 'marginTop') + $jc.margin(el, 'marginBottom');

            if (d == undefined || old == d)
                return old;

            var w = !this.options.vertical ?
                d - $jc.margin(el, 'marginLeft') - $jc.margin(el, 'marginRight') :
                d - $jc.margin(el, 'marginTop') - $jc.margin(el, 'marginBottom');

            $(el).css(this.wh, w + 'px');

            return this.dimension(el);
        },

        clipping: function() {
            return !this.options.vertical ?
                this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) :
                this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth'));
        },

        index: function(i, s) {
            if (s == undefined)
                s = this.options.size;

            return Math.round((((i - 1) / s) - Math.floor((i - 1) / s)) * s) + 1;
        }
    });

    $jc.extend({
        /**
        * Gets/Sets the global default configuration properties.
        *
        * @name defaults
        * @descr Gets/Sets the global default configuration properties.
        * @type Hash
        * @param Hash d A set of key/value pairs to set as configuration properties.
        * @cat Plugins/jCarousel
        */
        defaults: function(d) {
            return $.extend(defaults, d || {});
        },

        margin: function(e, p) {
            if (!e)
                return 0;

            var el = e.jquery != undefined ? e[0] : e;

            if (p == 'marginRight' && $.browser.safari) {
                var old = { 'display': 'block', 'float': 'none', 'width': 'auto' }, oWidth, oWidth2;

                $.swap(el, old, function() { oWidth = el.offsetWidth; });

                old['marginRight'] = 0;
                $.swap(el, old, function() { oWidth2 = el.offsetWidth; });

                return oWidth2 - oWidth;
            }

            return $jc.intval($.css(el, p));
        },

        intval: function(v) {
            v = parseInt(v);
            return isNaN(v) ? 0 : v;
        }
    });

})(jQuery);


/*
* jQuery UI Accordion 1.6
* 
* Copyright (c) 2007 Jörn Zaefferer
*
* http://docs.jquery.com/UI/Accordion
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.accordion.js 4876 2008-03-08 11:49:04Z joern.zaefferer $
*
*/

(function($) {

    // If the UI scope is not available, add it
    $.ui = $.ui || {};

    $.fn.extend({
        accordion: function(options, data) {
            var args = Array.prototype.slice.call(arguments, 1);

            return this.each(function() {
                if (typeof options == "string") {
                    var accordion = $.data(this, "ui-accordion");
                    accordion[options].apply(accordion, args);
                    // INIT with optional options
                } else if (!$(this).is(".ui-accordion"))
                    $.data(this, "ui-accordion", new $.ui.accordion(this, options));
            });
        },
        // deprecated, use accordion("activate", index) instead
        activate: function(index) {
            return this.accordion("activate", index);
        }
    });

    $.ui.accordion = function(container, options) {

        // setup configuration
        this.options = options = $.extend({}, $.ui.accordion.defaults, options);
        this.element = container;

        $(container).addClass("ui-accordion");

        if (options.navigation) {
            var current = $(container).find("a").filter(options.navigationFilter);
            if (current.length) {
                if (current.filter(options.header).length) {
                    options.active = current;
                } else {
                    options.active = current.parent().parent().prev();
                    current.addClass("current");
                }
            }
        }

        // calculate active if not specified, using the first header
        options.headers = $(container).find(options.header);
        options.active = findActive(options.headers, options.active);

        if (options.fillSpace) {
            var maxHeight = $(container).parent().height();
            options.headers.each(function() {
                maxHeight -= $(this).outerHeight();
            });
            var maxPadding = 0;
            options.headers.next().each(function() {
                maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
            }).height(maxHeight - maxPadding);
        } else if (options.autoheight) {
            var maxHeight = 0;
            options.headers.next().each(function() {
                maxHeight = Math.max(maxHeight, $(this).outerHeight());
            }).height(maxHeight);
        }

        options.headers
		.not(options.active || "")
		.next()
		.hide();
        options.active.parent().andSelf().addClass(options.selectedClass);

        if (options.event)
            $(container).bind((options.event) + ".ui-accordion", clickHandler);
    };

    $.ui.accordion.prototype = {
        activate: function(index) {
            // call clickHandler with custom event
            clickHandler.call(this.element, {
                target: findActive(this.options.headers, index)[0]
            });
        },

        enable: function() {
            this.options.disabled = false;
        },
        disable: function() {
            this.options.disabled = true;
        },
        destroy: function() {
            this.options.headers.next().css("display", "");
            if (this.options.fillSpace || this.options.autoheight) {
                this.options.headers.next().css("height", "");
            }
            $.removeData(this.element, "ui-accordion");
            $(this.element).removeClass("ui-accordion").unbind(".ui-accordion");
        }
    }

    function scopeCallback(callback, scope) {
        return function() {
            return callback.apply(scope, arguments);
        };
    }

    function completed(cancel) {
        // if removed while animated data can be empty
        if (!$.data(this, "ui-accordion"))
            return;
        var instance = $.data(this, "ui-accordion");
        var options = instance.options;
        options.running = cancel ? 0 : --options.running;
        if (options.running)
            return;
        if (options.clearStyle) {
            options.toShow.add(options.toHide).css({
                height: "",
                overflow: ""
            });
        }
        $(this).triggerHandler("change.ui-accordion", [options.data], options.change);
    }

    function toggle(toShow, toHide, data, clickedActive, down) {
        var options = $.data(this, "ui-accordion").options;
        options.toShow = toShow;
        options.toHide = toHide;
        options.data = data;
        var complete = scopeCallback(completed, this);

        // count elements to animate
        options.running = toHide.size() == 0 ? toShow.size() : toHide.size();

        if (options.animated) {
            if (!options.alwaysOpen && clickedActive) {
                $.ui.accordion.animations[options.animated]({
                    toShow: jQuery([]),
                    toHide: toHide,
                    complete: complete,
                    down: down,
                    autoheight: options.autoheight
                });
            } else {
                $.ui.accordion.animations[options.animated]({
                    toShow: toShow,
                    toHide: toHide,
                    complete: complete,
                    down: down,
                    autoheight: options.autoheight
                });
            }
        } else {
            if (!options.alwaysOpen && clickedActive) {
                toShow.toggle();
            } else {
                toHide.hide();
                toShow.show();
            }
            complete(true);
        }
    }

    function clickHandler(event) {
        var options = $.data(this, "ui-accordion").options;
        if (options.disabled)
            return false;

        // called only when using activate(false) to close all parts programmatically
        if (!event.target && !options.alwaysOpen) {
            options.active.parent().andSelf().toggleClass(options.selectedClass);
            var toHide = options.active.next(),
			data = {
			    instance: this,
			    options: options,
			    newHeader: jQuery([]),
			    oldHeader: options.active,
			    newContent: jQuery([]),
			    oldContent: toHide
			},
			toShow = options.active = $([]);
            toggle.call(this, toShow, toHide, data);
            return false;
        }
        // get the click target
        var clicked = $(event.target);

        // due to the event delegation model, we have to check if one
        // of the parent elements is our actual header, and find that
        if (clicked.parents(options.header).length)
            while (!clicked.is(options.header))
            clicked = clicked.parent();

        var clickedActive = clicked[0] == options.active[0];

        // if animations are still active, or the active header is the target, ignore click
        if (options.running || (options.alwaysOpen && clickedActive))
            return false;
        if (!clicked.is(options.header))
            return;

        // switch classes
        options.active.parent().andSelf().toggleClass(options.selectedClass);
        if (!clickedActive) {
            clicked.parent().andSelf().addClass(options.selectedClass);
        }

        // find elements to show and hide
        var toShow = clicked.next(),
		toHide = options.active.next(),
        //data = [clicked, options.active, toShow, toHide],
		data = {
		    instance: this,
		    options: options,
		    newHeader: clicked,
		    oldHeader: options.active,
		    newContent: toShow,
		    oldContent: toHide
		},
		down = options.headers.index(options.active[0]) > options.headers.index(clicked[0]);

        options.active = clickedActive ? $([]) : clicked;
        toggle.call(this, toShow, toHide, data, clickedActive, down);

        return false;
    };

    function findActive(headers, selector) {
        return selector != undefined
		? typeof selector == "number"
			? headers.filter(":eq(" + selector + ")")
			: headers.not(headers.not(selector))
		: selector === false
			? $([])
			: headers.filter(":eq(0)");
    }

    $.extend($.ui.accordion, {
        defaults: {
            selectedClass: "selected",
            alwaysOpen: true,
            animated: 'slide',
            event: "click",
            header: "a",
            autoheight: true,
            running: 0,
            navigationFilter: function() {
                return this.href.toLowerCase() == location.href.toLowerCase();
            }
        },
        animations: {
            slide: function(options, additions) {
                options = $.extend({
                    easing: "swing",
                    duration: 300
                }, options, additions);
                if (!options.toHide.size()) {
                    options.toShow.animate({ height: "show" }, options);
                    return;
                }
                var hideHeight = options.toHide.height(),
				showHeight = options.toShow.height(),
				difference = showHeight / hideHeight;
                options.toShow.css({ height: 0, overflow: 'hidden' }).show();
                options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate({ height: "hide" }, {
                    step: function(now) {
                        var current = (hideHeight - now) * difference;
                        if ($.browser.msie || $.browser.opera) {
                            current = Math.ceil(current);
                        }
                        options.toShow.height(current);
                    },
                    duration: options.duration,
                    easing: options.easing,
                    complete: function() {
                        if (!options.autoheight) {
                            options.toShow.css("height", "auto");
                        }
                        options.complete();
                    }
                });
            },
            bounceslide: function(options) {
                this.slide(options, {
                    easing: options.down ? "bounceout" : "swing",
                    duration: options.down ? 1000 : 200
                });
            },
            easeslide: function(options) {
                this.slide(options, {
                    easing: "easeinout",
                    duration: 700
                })
            }
        }
    });

})(jQuery);
/*
* jqModal - Minimalist Modaling with jQuery
*   (http://dev.iceburg.net/jquery/jqModal/)
*
* Copyright (c) 2007,2008 Brice Burgess <bhb@iceburg.net>
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
* 
* $Version: 03/01/2009 +r14
*/
(function($) {
    $.fn.jqm = function(o) {
        var p = {
            overlay: 50,
            overlayClass: 'jqmOverlay',
            closeClass: 'jqmClose',
            trigger: '.jqModal',
            ajax: F,
            ajaxText: '',
            target: F,
            modal: F,
            toTop: F,
            onShow: F,
            onHide: F,
            onLoad: F
        };
        return this.each(function() {
            if (this._jqm) return H[this._jqm].c = $.extend({}, H[this._jqm].c, o); s++; this._jqm = s;
            H[s] = { c: $.extend(p, $.jqm.params, o), a: F, w: $(this).addClass('jqmID' + s), s: s };
            if (p.trigger) $(this).jqmAddTrigger(p.trigger);
        });
    };

    $.fn.jqmAddClose = function(e) { return hs(this, e, 'jqmHide'); };
    $.fn.jqmAddTrigger = function(e) { return hs(this, e, 'jqmShow'); };
    $.fn.jqmShow = function(t) { return this.each(function() { t = t || window.event; $.jqm.open(this._jqm, t); }); };
    $.fn.jqmHide = function(t) { return this.each(function() { t = t || window.event; $.jqm.close(this._jqm, t) }); };

    $.jqm = {
        hash: {},
        open: function(s, t) {
            var h = H[s], c = h.c, cc = '.' + c.closeClass, z = (parseInt(h.w.css('z-index'))), z = (z > 0) ? z : 3000, o = $('<div></div>').css({ height: '100%', width: '100%', position: 'fixed', left: 0, top: 0, 'z-index': z - 1, opacity: c.overlay / 100 }); if (h.a) return F; h.t = t; h.a = true; h.w.css('z-index', z);
            if (c.modal) { if (!A[0]) L('bind'); A.push(s); }
            else if (c.overlay > 0) h.w.jqmAddClose(o);
            else o = F;

            h.o = (o) ? o.addClass(c.overlayClass).prependTo('body') : F;
            if (ie6) { $('html,body').css({ height: '100%', width: '100%' }); if (o) { o = o.css({ position: 'absolute' })[0]; for (var y in { Top: 1, Left: 1 }) o.style.setExpression(y.toLowerCase(), "(_=(document.documentElement.scroll" + y + " || document.body.scroll" + y + "))+'px'"); } }

            if (c.ajax) {
                var r = c.target || h.w, u = c.ajax, r = (typeof r == 'string') ? $(r, h.w) : $(r), u = (u.substr(0, 1) == '@') ? $(t).attr(u.substring(1)) : u;
                r.html(c.ajaxText).load(u, function() { if (c.onLoad) c.onLoad.call(this, h); if (cc) h.w.jqmAddClose($(cc, h.w)); e(h); });
            }
            else if (cc) h.w.jqmAddClose($(cc, h.w));

            if (c.toTop && h.o) h.w.before('<span id="jqmP' + h.w[0]._jqm + '"></span>').insertAfter(h.o);
            (c.onShow) ? c.onShow(h) : h.w.show(); e(h); return F;
        },
        close: function(s) {
            var h = H[s]; if (!h.a) return F; h.a = F;
            if (A[0]) { A.pop(); if (!A[0]) L('unbind'); }
            if (h.c.toTop && h.o) $('#jqmP' + h.w[0]._jqm).after(h.w).remove();
            if (h.c.onHide) h.c.onHide(h); else { h.w.hide(); if (h.o) h.o.remove(); } return F;
        },
        params: {}
    };
    var s = 0, H = $.jqm.hash, A = [], ie6 = $.browser.msie && ($.browser.version == "6.0"), F = false,
i = $('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({ opacity: 0 }),
e = function(h) { if (ie6) if (h.o) h.o.html('<p style="width:100%;height:100%"/>').prepend(i); else if (!$('iframe.jqm', h.w)[0]) h.w.prepend(i); f(h); },
f = function(h) { try { $(':input:visible', h.w)[0].focus(); } catch (_) { } },
L = function(t) { $()[t]("keypress", m)[t]("keydown", m)[t]("mousedown", m); },
m = function(e) { var h = H[A[A.length - 1]], r = (!$(e.target).parents('.jqmID' + h.s)[0]); if (r) f(h); return !r; },
hs = function(w, t, c) {
    return w.each(function() {
        var s = this._jqm; $(t).each(function() {
            if (!this[c]) { this[c] = []; $(this).click(function() { for (var i in { jqmShow: 1, jqmHide: 1 }) for (var s in this[i]) if (H[this[i][s]]) H[this[i][s]].w[i](this); return F; }); } this[c].push(s);
        });
    });
};
})(jQuery);


/*
* MojoZoom 0.1.4 - JavaScript Image Zoom
* Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/
* Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
*/

var MojoZoom = (function() {

    var $ = function(id) { return document.getElementById(id); };
    var dc = function(tag) { return document.createElement(tag); };

    var defaultWidth = 240;
    var defaultHeight = 344;

    function addEvent(element, ev, handler) {
        var doHandler = function(e) {
            return handler(e || window.event);
        }
        if (element.addEventListener) {
            element.addEventListener(ev, doHandler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + ev, doHandler);
        }
    }

    function getElementPos(element) {
        var x = element.offsetLeft;
        var y = element.offsetTop;
        var parent = element.offsetParent;
        while (parent) {
            x += parent.offsetLeft;
            y += parent.offsetTop;
            parent = parent.offsetParent;
        }
        return {
            x: x,
            y: y
        }
    }

    function getEventMousePos(element, e) {
        var scrollX = document.body.scrollLeft || document.documentElement.scrollLeft;
        var scrollY = document.body.scrollTop || document.documentElement.scrollTop;

        if (e.currentTarget) {
            var pos = getElementPos(element);
            return {
                x: e.clientX - pos.x + scrollX,
                y: e.clientY - pos.y + scrollY
            }
        }
        return {
            x: e.offsetX,
            y: e.offsetY
        }
    }

    function makeZoomable(img, zoomSrc, zoomImgCtr, zoomWidth, zoomHeight, alwaysShow) {
        // make sure the image is loaded, if not then add an onload event and return
        if (!img.complete && !img.__mojoZoomQueued) {
            addEvent(img, "load", function() {
                img.__mojoZoomQueued = true;
                setTimeout(function() {
                    makeZoomable(img, zoomSrc, zoomImgCtr, zoomWidth, zoomHeight, alwaysShow);
                }, 1);
            });
            return;
        }

        img.__mojoZoomQueued = false;

        // Wrap everything in a timeout.
        // this fixes a problem where, if makeZoomable is called a second time after changing the src,
        // FF would not have figured out the new offsetHeight of the image yet. A small timeout helps though it's rather hackish.
        setTimeout(function() {

            // sorry
            var isIE = !!document.all && !!window.attachEvent && !window.opera;

            var w = img.offsetWidth;
            var h = img.offsetHeight;

            var oldParent = img.parentNode;
            if (oldParent.nodeName != "A") {
                var linkParent = dc("a");
                linkParent.setAttribute("href", zoomSrc);
                oldParent.replaceChild(linkParent, img);
                linkParent.appendChild(img);
            } else {
                var linkParent = oldParent;
            }

            linkParent.style.position = "relative";
            linkParent.style.display = "block";
            linkParent.style.width = w + "px";
            linkParent.style.height = h + "px";

            var imgLeft = img.offsetLeft;
            var imgTop = img.offsetTop;

            var zoom = dc("div");
            zoom.className = "mojozoom_marker";

            var zoomImg = dc("img");
            zoomImg.className = "mojozoom_img";

            zoomImg.style.position = "absolute";
            zoomImg.style.left = "-9999px";
            zoomImg.style.top = "-9999px";

            document.body.appendChild(zoomImg);

            var parent = img.parentNode;

            var ctr = dc("div");
            with (ctr.style) {
                position = "absolute";
                left = imgLeft + "px";
                top = imgTop + "px";
                width = w + "px";
                height = h + "px";
                overflow = "hidden";
                display = "none";
            }

            ctr.appendChild(zoom);
            parent.appendChild(ctr);

            var zoomInput = parent;

            // clear old overlay
            if (img.__mojoZoomOverlay)
                parent.removeChild(img.__mojoZoomOverlay);
            img.__mojoZoomOverlay = ctr;

            // clear old high-res image
            if (img.__mojoZoomImage && img.__mojoZoomImage.parentNode)
                img.__mojoZoomImage.parentNode.removeChild(img.__mojoZoomImage);
            img.__mojoZoomImage = zoomImg;

            var useDefaultCtr = false;
            if (!zoomImgCtr) {
                zoomImgCtr = dc("div");
                zoomImgCtr.className = "mojozoom_imgctr";

                var imgPos = getElementPos(img);
                zoomImgCtr.style.left = w + imgPos.x + "px";
                zoomImgCtr.style.top = imgPos.y + "px";

                zoomImgCtr.style.width = ( defaultWidth) + "px";
                zoomImgCtr.style.height = (defaultHeight) + "px";

                document.body.appendChild(zoomImgCtr);
                useDefaultCtr = true;
            }
            zoomImgCtr.style.overflow = "hidden";

            if (!alwaysShow) {
                zoomImgCtr.style.visibility = "hidden";
            }

            addEvent(zoomImg, "load", function() {

                // bail out if img has been removed from dom
                if (!zoomImg.parentNode) return;

                var zoomWidth = zoomImg.offsetWidth;
                var zoomHeight = zoomImg.offsetHeight;

                var ctrWidth = zoomImgCtr.offsetWidth;
                var ctrHeight = zoomImgCtr.offsetHeight;

                var ratioW = zoomWidth / w;
                var ratioH = zoomHeight / h;

                var markerWidth = Math.round(ctrWidth / ratioW);
                var markerHeight = Math.round(ctrHeight / ratioH);

                document.body.removeChild(zoomImg);
                zoomImgCtr.appendChild(zoomImg);

                var zoomFill = dc("div");
                zoomFill.className = "mojozoom_fill";
                zoom.appendChild(zoomFill);

                var zoomBorder = dc("div");
                zoomBorder.className = "mojozoom_border";
                zoom.appendChild(zoomBorder);

                zoom.style.width = markerWidth + "px";
                zoom.style.height = markerHeight + "px";


                if (alwaysShow) {
                    zoom.style.left = "0px";
                    zoom.style.top = "0px";

                    zoomImg.style.left = "0px";
                    zoomImg.style.top = "0px";
                }

                var isInImage = false;

                if (!alwaysShow) {
                    addEvent(zoomInput, "mouseout",
					function(e) {
					    var target = e.target || e.srcElement;
					    if (!target) return;
					    if (target.nodeName != "DIV") return;
					    var relTarget = e.relatedTarget || e.toElement;
					    if (!relTarget) return;
					    while (relTarget != target && relTarget.nodeName != "BODY" && relTarget.parentNode) {
					        relTarget = relTarget.parentNode;
					    }
					    if (relTarget != target) {
					        isInImage = false;
					        ctr.style.display = "none";
					        zoomImgCtr.style.visibility = "hidden";
					    }
					}
				);
                    // a bit of a hack, mouseout is sometimes not caught if moving mouse really fast
                    addEvent(document.body, "mouseover",
					function(e) {
					    if (isInImage && !(e.toElement == zoomBorder || e.target == zoomBorder)) {
					        ctr.style.display = "none";
					        zoomImgCtr.style.visibility = "hidden";
					        isInImage = false;
					    }
					}
				);
                }

                addEvent(zoomInput, "mousemove",
				function(e) {
				    isInImage = true;

				    var imgPos = getElementPos(img);

				    if (useDefaultCtr) {
				        zoomImgCtr.style.left = w + imgPos.x + "px";
				        zoomImgCtr.style.top = imgPos.y + "px";
				    }
				    ctr.style.display = "block";
				    zoomImgCtr.style.visibility = "visible";

				    var pos = getEventMousePos(zoomInput, e);
				    if (e.srcElement && isIE) {
				        if (e.srcElement == zoom) return;
				        if (e.srcElement != zoomInput) {
				            var zoomImgPos = getElementPos(e.srcElement);
				            pos.x -= (imgPos.x - zoomImgPos.x);
				            pos.y -= (imgPos.y - zoomImgPos.y);
				        }
				    }
				    var x = markerWidth / 2;
				    var y = markerHeight / 2;

				    if (!isIE) {
				        pos.x -= imgLeft;
				        pos.y -= imgTop;
				    }

				    if (pos.x < x) pos.x = x;
				    if (pos.x > w - x) pos.x = w - x;
				    if (pos.y < y) pos.y = y;
				    if (pos.y > h - y) pos.y = h - y;

				    var left = ((pos.x - x) | 0);
				    var top = ((pos.y - y) | 0);

				    zoom.style.left = left + "px";
				    zoom.style.top = top + "px";

				    zoomImg.style.left = -((pos.x * ratioW - ctrWidth / 2) | 0) + "px";
				    zoomImg.style.top = -((pos.y * ratioH - ctrHeight / 2) | 0) + "px";
				}
			);
            });

            // I've no idea. Simply setting the src will make IE screw it self into a 100% CPU fest. In a timeout, it's ok.
            setTimeout(function() {
                zoomImg.src = zoomSrc;
            }, 1);

        }, 1);
    }

    function init() {
        var images = document.getElementsByTagName("img");
        for (var i = 0; i < images.length; i++) {
            var img = images[i];
            var zoomSrc = img.getAttribute("data-zoomsrc");
            if (zoomSrc) {
                makeZoomable(img, zoomSrc, document.getElementById(img.getAttribute("id") + "_zoom"), null, null, img.getAttribute("data-zoomalwaysshow") == "true");
            }
        }
    }

    return {
        addEvent: addEvent,
        init: init,
        makeZoomable: makeZoomable
    };

})();

MojoZoom.addEvent(window, "load", MojoZoom.init);
(function($) {

    $.fn.select_skin = function(w) {
        return $(this).each(function(i) {
            s = $(this);

            // create the container
            s.wrap('<div class="skinned-select"></div>');
            c = s.parent();
            c.children().before('<div class="skinned-text">&nbsp;</div>').each(function() {
                if (this.selectedIndex >= 0) $(this).prev().text(this.options[this.selectedIndex].innerHTML)
            });
            c.width(s.outerWidth() - 2);
            c.height(s.outerHeight() - 2);

            // skin the container
            c.css('background-color', s.css('background-color'));
            c.css('position', 'relative');

            // hide the original select
            s.css({ 'opacity': 0, 'position': 'relative', 'z-index': 100 });

            // get and skin the text label
            var t = c.children().prev();
            t.height(c.outerHeight() - s.css('padding-top').replace(/px,*\)*/g, "") - s.css('padding-bottom').replace(/px,*\)*/g, "") - t.css('padding-top').replace(/px,*\)*/g, "") - t.css('padding-bottom').replace(/px,*\)*/g, "") - 2);
            t.width(c.innerWidth() - s.css('padding-right').replace(/px,*\)*/g, "") - s.css('padding-left').replace(/px,*\)*/g, "") - t.css('padding-right').replace(/px,*\)*/g, "") - t.css('padding-left').replace(/px,*\)*/g, "") - c.innerHeight());
            // t.css('color', s.css('color'));
            t.css('font-size', s.css('font-size'));
            // t.css('font-family', s.css('font-family'));
            t.css('font-style', s.css('font-style'));
            t.css({ 'opacity': 100, 'overflow': 'hidden', 'position': 'absolute', 'text-indent': '0px', 'z-index': 1, 'top': 0, 'left': 0 });

            // add events
            c.children().click(function() {
                t.text(this.options[this.selectedIndex].innerHTML.replace("&amp;", "&"));
            });
            c.children().change(function() {
                t.text(this.options[this.selectedIndex].innerHTML.replace("&amp;", "&"));
            });
        });
    }
} (jQuery));

/*
hash function. returns a key,value object
*/
function hash() {
    this.length = 0;
    this.items = new Array();

    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof (arguments[i + 1]) != 'undefined') {
            this.items[arguments[i]] = arguments[i + 1];
            this.length++;
        }
    }

    this.removeItem = function(in_key) {
        var tmp_previous;
        if (typeof (this.items[in_key]) != 'undefined') {
            this.length--;
            var tmp_previous = this.items[in_key];
            delete this.items[in_key];
        }

        return tmp_previous;
    }

    this.getItem = function(in_key) {
        return this.items[in_key];
    }

    this.setItem = function(in_key, in_value) {
        var tmp_previous;
        if (typeof (in_value) != 'undefined') {
            if (typeof (this.items[in_key]) == 'undefined') {
                this.length++;
            }
            else {
                tmp_previous = this.items[in_key];
            }

            this.items[in_key] = in_value;
        }

        return tmp_previous;
    }

    this.hasItem = function(in_key) {
        return typeof (this.items[in_key]) != 'undefined';
    }

   

    this.clear = function() {
        for (var i in this.items) {
            delete this.items[i];
        }

        this.length = 0;
    }
}




//END PLUGINS

