(function($) {
    var ANIM_ATTRS = {
            opacity: "toggle",
            height: "toggle"
        },
        DEFAULTS = {
            itemClass: "a",
            injectClass: "gallery-injector-elem",
            selectedClass: "selected",
            inject: function() { return $('<span/>'); },
            onInject: null,
            onComplete: null,
            onRemove: null,
            single: true,
            speed: 1000,
            scroll: true,
            scrollSpeed: 1000,
            scrollOffset: 0,
            closeOnClick: false
        };

    var removeElem = function(elem, options, complete) {
            var parent = $(this),
                source = $(elem.data("elem"));
            $(elem).animate(ANIM_ATTRS, options.speed, function() {
                parent.removeData("cur").removeClass(options.selectedClass);
                source.removeClass(options.selectedClass);
                $(this).remove();
                if ($.isFunction(options.onRemove)) {
                    options.onRemove.call(parent, source);
                }
                if ($.isFunction(complete)) {
                    complete.call(this, source);
                }
            });
        },
        showElem = function(source, prev, elem, options) {
            var parent = $(this),
                show = function() {
                    prev.after(elem);
                    $(elem).animate(ANIM_ATTRS, 0, function() {
                        parent.data("cur", elem);
                        $(this).animate(ANIM_ATTRS, options.speed, function() {
                            source.addClass(options.selectedClass);
                            parent.addClass(options.selectedClass);
                            var h = source.offset().top + options.scrollOffset;
                            h = Math.min(h, $(document).height() - $(window).height());
                            if (options.scroll) {
                                $('html,body').animate({scrollTop: h}, options.scrollSpeed);
                            }
                            if ($.isFunction(options.onComplete)) {
                                options.onComplete.call(elem, parent, source, options);
                            }
                        });
                        if (options.closeOnClick) {
                            $(this).click(function() {
                                removeElem.call(parent, $(this), options);
                                return false;
                            });
                        }
                        if ($.isFunction(options.onInject)) {
                            options.onInject.call(elem, parent, source, options);
                        }
                    });
                };
            if (options.single && parent.data("cur")) {
                removeElem.call(parent, $(parent.data("cur")), options, show);
            } else {
                show();
            }
        };

    $.fn.galleryInjector = function(options) {
        options = $.extend(DEFAULTS, options);

        return this.each(function() {
            var parent = $(this),
                items = $(options.itemClass, parent);
            //("a", items).click(function() { return false; });
            items.click(function() {
                var source = $(this),
                    prev = source,
                    prevH = prev.offset().top,
                    next = prev,
                    nextH = prevH;
                while (nextH === prevH) {
                    prev = next;
                    next = next.next();
                    if (!next.length) {
                        break;
                    }
                    nextH = next.offset().top;
                }
                var galleryItem = options.inject.call(parent, source, prev)
                        .data("elem", source.get(0))
                        .addClass(options.injectClass),
                    isInjected = next.hasClass(options.injectClass);
                if (!next.length || (next.is(options.itemClass) && !isInjected)) {
                    showElem.call(parent, source, prev, galleryItem, options);
                } else if (isInjected && !next.is(":animated")) {
                    var sameElem = next.data("elem") === galleryItem.data("elem");
                    removeElem.call(parent, next, options, function() {
                        if (!sameElem) {
                            showElem.call(parent, source, prev, galleryItem, options);
                        }
                    });
                }
                return false;
            });
        });
    };
})(jQuery);

