(function ($) {

    $.extend(com.luigibormioli.products, {
        CategoryList: function () {

            var $categories, $default, $window, active, categories, reset;

            (function () {

                $categories = $('ul#categories-list');

                // REFACTOR: PERFORMANCE
                $default = $categories.find('li.default');

                $window = $(window).one('CATEGORY_VIEW_OPEN', open);

                active = null;

                categories = {};

                reset = new com.luigibormioli.products.CategoryListReset;

                $categories
                    .bind('CATEGORY_VIEW_OPEN', function (event, categoryIdentifier) {

                        $window.trigger('VIEW_CONTROLLER_OPEN', [new com.luigibormioli.products.CategoryView(categoryIdentifier)]);

                        return false;

                    })
                    .bind('mouseenter', function () {

                        $categories.addClass('hover');

                    })
                    .bind('mouseleave', function () {

                        $categories.removeClass('hover');

                    })
                    // REFACTOR: PERFORMANCE
                    .find('li:not(li.default)')
                    .each(function () {

                        var category = new com.luigibormioli.products.CategoryListCategory($(this));

                        categories[category._identifier] = category;

                    });

            })();

            function close () {

                $window
                    .unbind('COLLECTION_BROWSER_UNFILTER', close)
                    .unbind('VIEW_CONTROLLER_ERROR', close)
                    .one('CATEGORY_VIEW_OPEN', open);

                reset.disable();

                active.deselect();

                active = null;

                $default.addClass('selected');

            }

            function open (event, categoryIdentifier) {

                if (categories[categoryIdentifier]) {

                    $window
                        .one('COLLECTION_BROWSER_UNFILTER', close)
                        .one('VIEW_CONTROLLER_ERROR', close);

                    reset.enable();

                    $default.removeClass('selected');

                    active = categories[categoryIdentifier];

                    active.select();

                }

            }

        },
        CategoryListCategory: function ($category) {

            var $link, identifier;

            (function () {

                $link = $category.find('a');

                identifier = $link.attr('rel');

                enable();

            })();

            this._identifier = identifier;

            this.deselect = deselect;

            this.select = select;

            function deselect () {

                enable();

                $category.removeClass('selected');

            }

            function disable () {

                $link.unbind('click', open);

            }

            function enable () {

                $link.bind('click', open);

            }

            function open () {

                $category.trigger('CATEGORY_VIEW_OPEN', [identifier]);

            }

            function select () {

                disable();

                $category.addClass('selected');

            }

        },
        CategoryListReset: function () {

            var $reset, $window;

            (function () {

                $reset = $('a#categories-reset');

                $window = $(window);

            })();

            this.disable = disable;

            this.enable = enable;

            function disable () {

                $reset
                    .unbind('click', reset)
                    .animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                        $reset.css({visibility: 'hidden'});

                    });

            }

            function enable () {

                $reset
                    .stop()
                    .one('click', reset)
                    .css({visibility: 'visible'})
                    .animate({opacity: 1}, 500, 'easeInOutQuad');

            }

            function reset () {

                $window
                    .trigger('COLLECTION_BROWSER_UNFILTER')
                    .trigger('VIEW_CONTROLLER_CLOSE');

            }

        },
        ViewController: function () {

            var $body, $content, $view, $window, active, position;

            (function () {

                $body = $('html, body').attr('scrollTop', 0);

                $content = $('div#view-content');

                $view = $('div#view');

                $window = $(window)
                    .bind('VIEW_CONTROLLER_OPEN', open)
                    .bind('VIEW_CONTROLLER_SUCCESS', function (event, scroll) {

                        load(scroll);

                    })
                    .bind('VIEW_CONTROLLER_ERROR', function (event, message) {

                        $content.html('<div id="view-description">' + message + '</div>');

                        load();

                    });

                active = null;

                position = $content.offset().top - 24;

                $.ajaxSetup({dataType: 'json', global: false, url: '/products/view/', timeout: 5000});

            })();

            function close () {

                // DEBUG
                if ($.browser.msie) $content.css({visibility: 'hidden'});

                $view
                    .queue([])
                    .stop()
                    .animate({opacity: 0}, 500, 'easeInOutQuad')
                    .animate({height: 0}, 1000, 'easeInOutExpo', function () {

                        $content
                            .css({visibility: 'hidden'})
                            .empty();

                        $view.css({backgroundImage: 'url(/include/img/loading.gif)', overflow: 'hidden'});

                    });

            }

            function load (scroll) {

                $view
                    .animate({opacity: 0}, 500, 'easeInOutQuad')
                    .animate({height: $content.height()}, 1000, 'easeInOutExpo', function () {

                        if (scroll) $body.animate({scrollTop: position}, 1000, 'easeInOutQuad');

                        $content.css({visibility: 'visible'});

                        $view
                            .css({backgroundImage: 'none', overflow: 'visible'})
                            .animate({opacity: 1}, 500, 'easeInOutQuad');

                        $window.trigger('VIEW_CONTROLLER_LOAD');

                    });

            }

            function open (event, view) {

                $body.animate({scrollTop: 0}, 1000, 'easeInOutQuad');

                if (active) $window.trigger('VIEW_CONTROLLER_CLOSE');

                active = view;

                $window.one('VIEW_CONTROLLER_CLOSE', close);

                $view
                    .animate({height: 72}, 500, 'easeInOutQuad')
                    .animate({opacity: 1}, 500, 'easeInOutQuad', request);

            }

            function request () {

                var cached = $.cache($.ajaxSettings.url + '?' + $.param($.ajaxSettings.data)),

                    success = $.ajaxSettings.success;

                if (cached) success.apply(cached.scope, cached.arguments);

                else {

                    $.ajaxSetup({success: function (response) {

                        if (response) {

                            $.cache(this.url, {scope: this, arguments: arguments});

                            success.apply(this, arguments);

                        }

                        else $.ajaxSettings.error.call(this);

                    }});

                    $.ajax();

                }

            }

        },
        CollectionView: function (collectionIdentifier, itemIdentifier) {

            var $window;

            (function () {

                $window = $(window).one('VIEW_CONTROLLER_OPEN', function () {

                    $window.trigger('COLLECTION_VIEW_OPEN', [collectionIdentifier]);

                });

                $.ajaxSetup({
                    data: {collection: collectionIdentifier},
                    success: function (response) {

                        $('div#view-content').html(response.view);

                        var $description = $('div#view-description'),

                            $items = $('ul#view-items-list'),

                            $reset = $('h2#view-reset a').bind('click', reset),

                            $title = $('h1#view-title'),

                            $subtitle = $title.find('span'),

                            active = null,

                            description = $description.html(),

                            guarantee = new com.luigibormioli.products.Guarantee,

                            items = {},

                            loading = 0,

                            scroller = new com.luigibormioli.products.CollectionViewItemScroller;

                        $description.height($description.height());

                        $window
                            .one('VIEW_CONTROLLER_LOAD', open)
                            .one('VIEW_CONTROLLER_CLOSE', close)
                            .bind('COLLECTION_VIEW_SEEK', seek);

                        $items
                            .bind('COLLECTION_VIEW_ITEM_LOAD', load)
                            .bind('COLLECTION_VIEW_SEEK', seek)
                            .bind('COLLECTION_VIEW_FOCUS', focus)
                            .find('li')
                            .each(function () {

                                ++loading;

                                var item = new com.luigibormioli.products.CollectionViewItem($(this));

                                items[item._identifier] = item;

                            });

                        function close () {

                            $window
                                .unbind('VIEW_CONTROLLER_LOAD', open)
                                .unbind('COLLECTION_VIEW_SEEK', seek);

                            $items
                                .unbind('COLLECTION_VIEW_ITEM_LOAD', load)
                                .unbind('COLLECTION_VIEW_SEEK', seek)
                                .unbind('COLLECTION_VIEW_FOCUS', focus);

                            $reset.unbind('click', reset);

                        }

                        function focus (event, itemIdentifier) {

                            if (itemIdentifier) {

                                $subtitle
                                    .html(items[itemIdentifier]._title)
                                    .css({opacity: 1});

                            }

                            else {

                                $subtitle
                                    .html('')
                                    .css({opacity: 0});

                            }

                            return false;

                        }

                        function load () {

                            if (!--loading) {

                                $items.unbind('COLLECTION_VIEW_ITEM_LOAD', load);

                                $window.trigger('VIEW_CONTROLLER_SUCCESS', [true]);

                            }

                            return false;

                        }

                        function open () {

                            $window.trigger('COLLECTION_VIEW_SEEK', [itemIdentifier]);

                            return false;

                        }

                        function reset () {

                            $window.trigger('COLLECTION_VIEW_SEEK');

                        }

                        function seek (event, itemIdentifier) {

                            if (itemIdentifier) {

                                if (active) {

                                    if (itemIdentifier === active._identifier) return;

                                    items[active._identifier] = active;

                                    $subtitle.animate({opacity: 0}, 500, 'easeInOutQuad');

                                }

                                active = items[itemIdentifier];

                                delete items[itemIdentifier];

                                active.disable();

                                active.unfilter();

                                $.each(items, function () {

                                    this.disable();

                                    this.filter();

                                });

                                $description.animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                                    $subtitle
                                        .html(active._title)
                                        .animate({opacity: 1}, 500, 'easeInOutQuad');

                                    $reset
                                        .css({visibility: 'visible'})
                                        .animate({opacity: 1}, 500, 'easeInOutQuad');

                                    $description
                                        .html(active._description)
                                        .animate({opacity: 1}, 500, 'easeInOutQuad');

                                });

                                scroller.seek(active._position);

                            }

                            else {

                                if (active) {

                                    items[active._identifier] = active;

                                    active = null;

                                    $subtitle.animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                                        $subtitle.html('');

                                    });

                                    $reset.animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                                        $reset.css({visibility: 'hidden'});

                                    });

                                    $description.animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                                        $description
                                            .html(description)
                                            .animate({opacity: 1}, 500, 'easeInOutQuad');

                                    });


                                }

                                $.each(items, function () {

                                    this.enable();

                                    this.unfilter();

                                });

                                scroller.seek();

                            }

                            return false;

                        }

                    },
                    error: function () {

                        $window.trigger('VIEW_CONTROLLER_ERROR', ['An error occurred while loading a collection. Please try again.']);

                    }
                });

            })();

        },
        CollectionViewItem: function ($item) {

            var $link, description, identifier, position, timer, title;

            (function () {

                // REFACTOR: PERFORMANCE
                $link = $item.children('a');

                description = $item.find('p').html();

                identifier = $link.attr('rel');

                position = $item.offset().left;

                timer = null;

                title = '&nbsp;/&nbsp;' + $item.find('h2').html();

                $(new Image)
                    .one('load', function () {

                        $item.trigger('COLLECTION_VIEW_ITEM_LOAD');

                    })
                    .attr('src', $link.find('img').attr('src'));

                enable();

            })();

            this._description = description;

            this._identifier = identifier;

            this._position = position;

            this._title = title;

            this.disable = disable;

            this.enable = enable;

            this.filter = filter;

            this.seek = seek;

            this.unfilter = unfilter;

            function blur () {

                $item.trigger('COLLECTION_VIEW_FOCUS');

            }

            function disable() {

                if ($.browser.msie) {

                    stop();

                    $item
                        .unbind('mouseover', start)
                        .unbind('mouseout', blur);

                }

                else {

                    $item
                        .unbind('mouseover', focus)
                        .unbind('mouseout', blur);

                }

                $link
                    .unbind('click', seek)
                    .bind('click', reset);

            }

            function enable() {

                if ($.browser.msie) {

                    $item
                        .bind('mouseover', start)
                        .bind('mouseout', blur);

                }

                else {

                    $item
                        .bind('mouseover', focus)
                        .bind('mouseout', blur);

                }

                $link
                    .unbind('click', reset)
                    .bind('click', seek);

            }

            function filter () {

                $item.animate({opacity: .2}, 500, 'easeInOutQuad');

            }

            function focus () {

                $item.trigger('COLLECTION_VIEW_FOCUS', [identifier]);

            }

            function reset () {

                $item.trigger('COLLECTION_VIEW_SEEK');

            }

            function seek () {

                $item.trigger('COLLECTION_VIEW_SEEK', [identifier]);

            }

            function start () {

                $item.bind('mouseout', stop);

                timer = setTimeout(focus, 100);

            }

            function stop () {

                $item.unbind('mouseout', stop);

                clearTimeout(timer);

                timer = null;

            }

            function unfilter () {

                $item.animate({opacity: 1}, 500, 'easeInOutQuad');

            }

        },
        CollectionViewItemScroller: function () {

            var $items, $scrollbar, $scroller, $window, active, currentPosition, itemsWidth, maximumDuration, maximumPosition, minimumPosition, positionOffset, positionRatio, targetPosition, timer;

            (function () {

                $items = $('ul#view-items-list');

                $scrollbar = $('div#collection-browser-scrollbar');

                $scroller = $('div#view-items').bind('mousemove', function (event) {

                    targetPosition = (event.pageX * positionRatio) - positionOffset;

                });

                $window = $(window).bind('resize', function () {

                    calculate();

                    if (active) scroll(currentPosition, true);

                });

                active = true;

                currentPosition = $items.position().left;

                itemsWidth = $items.width();

                maximumDuration = 2000;

                maximumPosition = currentPosition;

                minimumPosition = null;

                positionOffset = currentPosition;

                positionRatio = null;

                targetPosition = null;

                timer = null;

                calculate();

                scroll(currentPosition);

            })();

            this.seek = seek;

            function calculate () {

                var scrollbarWidth = $scrollbar.width();

                minimumPosition = positionOffset;

                positionRatio = 0;

                if (itemsWidth > scrollbarWidth) {

                    minimumPosition -= itemsWidth - scrollbarWidth;

                    positionRatio = (itemsWidth - scrollbarWidth) / $window.width();

                }

            }

            function scroll (position, animate) {

                clearInterval(timer);

                timer = null;

                if (active) {

                    if (position < minimumPosition) position = minimumPosition;

                    else if (position > maximumPosition) position = maximumPosition;

                }

                if (animate) {

                    var duration = (Math.abs(position - currentPosition) / maximumPosition) * maximumDuration;

                    if (duration > maximumDuration) duration = maximumDuration;

                    $items
                        .stop()
                        .animate({left: position}, duration, 'easeOutQuint', (active) ? start : null);

                }

                else {

                    $items.css({left: position});

                    start();

                }

                currentPosition = position;

                targetPosition = position * -1;

            }

            function seek (position) {

                if (position) {

                    active = false;

                    position = (positionOffset - position) + positionOffset;

                }

                else {

                    active = true;

                    position = currentPosition;

                }

                scroll(position, true);

            }

            function start () {

                timer = setInterval(function () {

                    currentPosition -= (currentPosition - (targetPosition * -1)) / 10;

                    $items
                        .stop()
                        .css({left: currentPosition});

                }, 1);

            }

        },
        CategoryView: function (categoryIdentifier) {

            var $window;

            (function () {

                $window = $(window)
                    .one('VIEW_CONTROLLER_OPEN', function () {

                        $window.trigger('CATEGORY_VIEW_OPEN', [categoryIdentifier]);

                    })
                    .trigger('COLLECTION_BROWSER_UNFILTER');

                $.ajaxSetup({
                    data: {category: categoryIdentifier},
                    success: function (response) {

                        $('div#view-content').html(response.view);

                        $window
                            .trigger('COLLECTION_BROWSER_FILTER', [response.filter])
                            .trigger('VIEW_CONTROLLER_SUCCESS');

                    },
                    error: function () {

                        $window.trigger('VIEW_CONTROLLER_ERROR', ['An error occurred while loading a category. Please try again.']);

                    }
                });

            })();

        },
        CollectionBrowser: function () {

            var $window, active, collections, scrollbar;

            (function () {

                $window = $(window)
                    .one('COLLECTION_VIEW_OPEN', open)
                    .bind('COLLECTION_BROWSER_FILTER', function (event, results) {

                        var position = null;

                        $.each(collections, function (collectionIdentifier, collection) {

                            if (results[collectionIdentifier]) $.each(this._items, function () {

                                if (!results[collectionIdentifier][this._identifier]) this.filter();

                                else if (!position) position = collection._position;

                            });

                            else $.each(this._items, function () {

                                this.filter();

                            });

                        });

                        scrollbar.seek(position);

                    })
                    .bind('COLLECTION_BROWSER_UNFILTER', function () {

                        $.each(collections, function () {

                            $.each(this._items, function () {

                                this.unfilter();

                            });

                        });

                        scrollbar.seek();

                    });

                active = null;

                collections = {};

                scrollbar = new com.luigibormioli.products.CollectionBrowserScrollbar;

                $('div#collection-browser').bind('wheel', scrollbar.wheel);

                $('ul#collection-browser-list')
                    .bind('COLLECTION_VIEW_OPEN', function (event, collectionIdentifier, itemIdentifier) {

                        $window.trigger('VIEW_CONTROLLER_OPEN', [new com.luigibormioli.products.CollectionView(collectionIdentifier, itemIdentifier)]);

                        return false;

                    })
                    .bind('COLLECTION_VIEW_SEEK', function (event, itemIdentifier) {

                        $window.trigger('COLLECTION_VIEW_SEEK', [itemIdentifier]);

                        return false;

                    })
                    // REFACTOR: PERFORMANCE
                    .children('li')
                    .each(function () {

                        var collection = new com.luigibormioli.products.CollectionBrowserCollection($(this));

                        collections[collection._identifier] = collection;

                    });

            })();

            function close () {

                $window
                    .unbind('VIEW_CONTROLLER_ERROR', close)
                    .unbind('VIEW_CONTROLLER_CLOSE', close)
                    .one('COLLECTION_VIEW_OPEN', open);

                active.deselect();

                active = null;

            }

            function open (event, collectionIdentifier) {

                if (collections[collectionIdentifier]) {

                    $window
                        .one('VIEW_CONTROLLER_ERROR', close)
                        .one('VIEW_CONTROLLER_CLOSE', close);

                    active = collections[collectionIdentifier];

                    scrollbar.seek(active._position);

                    active.select();

                }

            }

        },
        CollectionBrowserScrollbar: function () {

            var $collections, $handle, $scrollbar, collectionsOffset, collectionsWidth, handleHalfWidth, handlePosition, handleWidth, maximumDuration, maximumPosition, minimumPosition, positionRatio;

            (function () {

                $collections = $('ul#collection-browser-list');

                $handle = $('a#collection-browser-scrollbar-handle')
                    .bind('dragstart', function () {

                        $handle.addClass('hover');

                    })
                    .bind('drag', function (event) {

                        var position = event.offsetX - collectionsOffset;

                        scroll(position);

                    })
                    .bind('dragend', function () {

                        $handle.removeClass('hover');

                        return false;

                    });

                $scrollbar = $('div#collection-browser-scrollbar')
                    .bind('mousedown', function (event) {

                        var position = event.pageX - handleHalfWidth - collectionsOffset;

                        scroll(position, true);

                    })
                    .bind('wheel', wheel);

                collectionsOffset = $collections.position().left;

                collectionsWidth = $collections.width();

                handlePosition = $handle.position().left;

                handleWidth = $handle.width();

                handleHalfWidth = handleWidth / 2;

                maximumDuration = 1000;

                maximumPosition = null;

                minimumPosition = handlePosition;

                positionRatio = null;

                $(window).bind('resize', function () {

                    var position = handlePosition / maximumPosition;

                    calculate();

                    position *= maximumPosition;

                    scroll(position, true);

                });

                calculate();

            })();

            this.seek = seek;

            this.wheel = wheel;

            function calculate () {

                var scrollbarWidth = $scrollbar.width();

                maximumPosition = scrollbarWidth - handleWidth;

                if (handlePosition > maximumPosition) $handle.css({left: maximumPosition});

                positionRatio = ((collectionsWidth - scrollbarWidth) / maximumPosition) * -1;

            }

            function seek (position) {

                if (position) scroll(Math.abs((position - collectionsOffset) / positionRatio), true);

                else scroll(minimumPosition, true);

            }

            function scroll (position, animate) {

                if (position <= minimumPosition) {

                    position = minimumPosition;

                    $handle.css({cursor: 'e-resize'});

                }

                else if (position >= maximumPosition) {

                    position = maximumPosition;

                    $handle.css({cursor: 'w-resize'});

                }

                else $handle.css({cursor: 'ew-resize'});

                var collectionsPosition = (position * positionRatio) + collectionsOffset;

                if (animate) {

                    $handle
                        .queue([])
                        .stop();


                    $collections
                        .queue([])
                        .stop();

                    var duration = (Math.abs(position - handlePosition) / maximumPosition) * maximumDuration;

                    if (duration > maximumDuration) duration = maximumDuration;

                    $handle.animate({left: position}, duration, 'easeOutQuint');

                    $collections.animate({left: collectionsPosition}, duration, 'easeOutQuint');

                }

                else {

                    $handle.css({left: position});

                    $collections.css({left: collectionsPosition});

                }

                handlePosition = position;

            }

            function wheel (event, delta) {

                scroll(handlePosition + (((delta < 0) ? 1 : -1) * handleWidth), true);

                return false;

            }

        },
        CollectionBrowserCollection: function ($collection) {

            var $title, $subtitle, items, identifier, position;

            (function () {

                $title = $collection.find('h3 a');

                $subtitle = $collection.find('h4 a');

                items = {};

                identifier = $title.attr('rel');

                position = $collection.offset().left;

                $collection
                    .find('ul')
                    .bind('COLLECTION_VIEW_OPEN', function (event, itemIdentifier) {

                        $collection.trigger('COLLECTION_VIEW_OPEN', [identifier, itemIdentifier]);

                        return false;

                    })
                    .bind('COLLECTION_VIEW_SEEK', function (event, itemIdentifier) {

                        $collection.trigger('COLLECTION_VIEW_SEEK', [itemIdentifier]);

                        return false;

                    })
                    .find('li')
                    .each(function () {

                        var item = new com.luigibormioli.products.CollectionBrowserItem($(this));

                        items[item._identifier] = item;

                    });

                enable();

            })();

            this._items = items;

            this._identifier = identifier;

            this._position = position;

            this.deselect = deselect;

            this.select = select;

            function blur () {

                $.each(items, function () {

                    this.hide();

                });

            }

            function deselect () {

                enable();

                $.each(items, function () {

                    this.enable();

                    this.hide();

                });

            }

            function disable () {

                $title
                    .unbind('click', open)
                    .unbind('mouseover', focus)
                    .unbind('mouseout', blur)
                    .bind('click', seek);

                $subtitle
                    .unbind('click', open)
                    .unbind('mouseover', focus)
                    .unbind('mouseout', blur)
                    .bind('click', seek);

            }

            function enable () {

                $title
                    .unbind('click', seek)
                    .bind('click', open)
                    .bind('mouseover', focus)
                    .bind('mouseout', blur);

                $subtitle
                    .unbind('click', seek)
                    .bind('click', open)
                    .bind('mouseover', focus)
                    .bind('mouseout', blur);

            }

            function focus () {

                $.each(items, function () {

                    this.show();

                });

            }

            function open (event, itemIdentifier) {

                $collection.trigger('COLLECTION_VIEW_OPEN', [identifier, itemIdentifier]);

            }

            function seek (event, itemIdentifier) {

                $collection.trigger('COLLECTION_VIEW_SEEK', [itemIdentifier]);

            }

            function select () {

                disable();

                $.each(items, function () {

                    this.disable();

                    this.show();

                });

            }

        },
        CollectionBrowserItem: function ($item) {

            var $image, $link, identifier, filtered;

            (function () {

                $link = $item
                    .find('a')
                    .animate({opacity: 1}, 500, 'easeInOutQuad');

                $image = $link.find('img');

                identifier = $link.attr('rel');

                filtered = false;

                var source = $image.attr('src');

                $(new Image)
                    .one('load', function () {

                        $link.animate({opacity: 0}, 500, 'easeInOutQuad', function () {

                                $image.css({visibility: 'visible'});

                                $link
                                    .css({background: 'transparent url(' + source + ') 0 -75px no-repeat'})
                                    .animate({opacity: (filtered) ? .465 : 1}, 500, 'easeInOutQuad');

                            });

                    })
                    .attr('src', source);

                enable();

            })();

            this._identifier = identifier;

            this.disable = disable;

            this.enable = enable;

            this.filter = filter;

            this.hide = hide;

            this.show = show;

            this.unfilter = unfilter;

            function disable () {

                $link
                    .unbind('mouseover', show)
                    .unbind('focus', show)
                    .unbind('mouseout', hide)
                    .unbind('blur', hide)
                    .unbind('click', open)
                    .bind('click', seek);

            }

            function enable () {

                $link
                    .unbind('click', seek)
                    .bind('mouseover', show)
                    .bind('focus', show)
                    .bind('mouseout', hide)
                    .bind('blur', hide)
                    .bind('click', open);

            }

            function filter () {

                filtered = true;

                $link.animate({opacity: .465}, 500, 'easeInOutQuad');

            }

            function hide () {

                if (filtered) $link.animate({opacity: .465}, 250, 'easeInOutQuad');

                $image.animate({opacity: 1}, 250, 'easeInOutQuad');

            }

            function open () {

                $item.trigger('COLLECTION_VIEW_OPEN', [identifier]);

            }

            function seek () {

                $item.trigger('COLLECTION_VIEW_SEEK', [identifier]);

            }

            function show () {

                if (filtered) $link
                    .queue([])
                    .stop()
                    .animate({opacity: 1}, 250, 'easeInOutQuad');

                $image
                    .queue([])
                    .stop()
                    .animate({opacity: 0}, 250, 'easeInOutQuad');

            }

            function unfilter () {

                filtered = false;

                $link.animate({opacity: 1}, 500, 'easeInOutQuad');

            }

        },
        CollectionList: function () {

            var $window, active, collections;

            (function () {

                $window = $(window).one('COLLECTION_VIEW_OPEN', open);

                active = null;

                collections = {};

                $('ul#collection-list-list')
                    .bind('COLLECTION_VIEW_OPEN', function (event, collectionIdentifier) {

                        $window.trigger('VIEW_CONTROLLER_OPEN', [new com.luigibormioli.products.CollectionView(collectionIdentifier)]);

                        return false;

                    })
                    .bind('COLLECTION_VIEW_SEEK', function () {

                        $window.trigger('COLLECTION_VIEW_SEEK');

                        return false;

                    })
                    .find('a')
                    .each(function () {

                        var collection = new com.luigibormioli.products.CollectionListCollection($(this));

                        collections[collection._identifier] = collection;

                    });

            })();

            function close () {

                $window
                    .unbind('VIEW_CONTROLLER_ERROR', close)
                    .unbind('VIEW_CONTROLLER_CLOSE', close)
                    .one('COLLECTION_VIEW_OPEN', open);

                active.enable();

                active = null;

            }

            function open (event, collectionIdentifier) {

                if (collections[collectionIdentifier]) {

                    $window
                        .one('VIEW_CONTROLLER_ERROR', close)
                        .one('VIEW_CONTROLLER_CLOSE', close);

                    active = collections[collectionIdentifier];

                    active.disable();

                }

            }

        },
        CollectionListCollection: function ($collection) {

            var identifier;

            (function () {

                identifier = $collection.attr('rel');

                enable();

            })();

            this._identifier = identifier;

            this.disable = disable;

            this.enable = enable;

            function disable () {

                $collection
                    .unbind('click', open)
                    .bind('click', seek);

            }

            function enable () {

                $collection
                    .unbind('click', seek)
                    .bind('click', open);

            }

            function open () {

                $collection.trigger('COLLECTION_VIEW_OPEN', [identifier]);

            }

            function seek () {

                $collection.trigger('COLLECTION_VIEW_SEEK');

            }

        }
    });

    $(function () {

        if (($.browser.msie && ($.browser.version < 6)) || ($.browser.safari && ($.browser.version < 500))) return false;

        new com.luigibormioli.products.CategoryList;

        new com.luigibormioli.products.ViewController;

        new com.luigibormioli.products.CollectionBrowser;

        new com.luigibormioli.products.CollectionList;

        var hash = location.hash.match(/^#\/(collection\/(\d+)(\/item\/(\d+))?|category\/(\d+))\/?$/);

        if (hash) {

            var view = (hash[2]) ?
                new com.luigibormioli.products.CollectionView(hash[2], (hash[4]) ? hash[4] : null) : ((hash[5]) ?
                    new com.luigibormioli.products.CategoryView(hash[5]) : null);

            if (view) $(window).trigger('VIEW_CONTROLLER_OPEN', [view]);

        }

    });

})(jQuery);