/**
 * jQuery VGrid v0.1.6 - variable grid layout plugin
 *
 * Terms of Use - jQuery VGrid
 * under the MIT (http://www.opensource.org/licenses/mit-license.php) License.
 *
 * Copyright 2009-2010 xlune.com All rights reserved.
 * (http://blog.xlune.com/2009/09/jqueryvgrid.html)
 */
(function($)
{
    function makePos(self)
    {
        var _childs = self.data("_vgchild");
        var _width = self.width();
        var _matrix = [[0,_width,0]];
        var _hmax=0, _c, _size, _point;
        _childs.each(function(i)
        {
            _c = $(this);
            _size = getSize(_c);
            _point = getAttachPoint(_matrix, _size[0]);
            _matrix = updateAttachArea(_matrix, _point, _size);
            _hmax = Math.max(_hmax, _point[1] + _size[1]);
            _c.data("_vgleft", _point[0]);
            _c.data("_vgtop", _point[1]);
        });
        self.data("_vgwrapheight", _hmax);
        heightTo(self);
    };
    function getAttachPoint(mtx, width)
    {
        var _mtx = mtx.concat().sort(matrixSortDepth);
        var _max = _mtx[_mtx.length-1][2];
        for(var i=0,imax=_mtx.length; i<imax; i++)
        {
            if(_mtx[i][2] >= _max) break;
            if(_mtx[i][1]-_mtx[i][0] >= width)
            {
                return [_mtx[i][0], _mtx[i][2]];
            }
        }
        return [0, _max];
    };
    function updateAttachArea(mtx, point, size)
    {
        var _mtx = mtx.concat().sort(matrixSortDepth);
        var _cell = [point[0], point[0]+size[0], point[1]+size[1]];
        for(var i=0,imax=_mtx.length; i<imax; i++)
        {
            if(_cell[0] <= _mtx[i][0] && _mtx[i][1] <= _cell[1])
            {
                delete _mtx[i];
            }
            else
            {
                _mtx[i] = matrixTrimWidth(_mtx[i], _cell);
            }
        }
        return matrixJoin(_mtx, _cell);
    };
    function matrixSortDepth(a, b)
    {
        if(!a || !b) return 0;
        return ((a[2] == b[2] && a[0] > b[0]) || a[2] > b[2]) ? 1 : -1;
    };
    function matrixSortX(a, b)
    {
        if(!a || !b) return 0;
        return (a[0] > b[0]) ? 1 : -1;
    };
    function matrixJoin(mtx, cell)
    {
        var _mtx = mtx.concat([cell]).sort(matrixSortX);
        var _mtx_join = [];
        for(var i=0,imax=_mtx.length; i<imax; i++)
        {
            if(!_mtx[i]) continue;
            if(_mtx_join.length > 0
                && _mtx_join[_mtx_join.length-1][1] == _mtx[i][0]
                && _mtx_join[_mtx_join.length-1][2] == _mtx[i][2])
            {
                _mtx_join[_mtx_join.length-1][1] = _mtx[i][1];
            }
            else
            {
                _mtx_join.push(_mtx[i]);
            }
        }
        return _mtx_join;
    };
    function matrixTrimWidth(a, b)
    {
        if(a[0] >= b[0] && a[0] < b[1] || a[1] >= b[0] && a[1] < b[1])
        {
            if(a[0] >= b[0] && a[0] < b[1])
            {
                a[0] = b[1];
            }
            else
            {
                a[1] = b[0];
            }
        }
        return a;
    };
    function getSize(child)
    {
        var _w = child.width();
        var _h = child.height();
        _w += Number(child.css("margin-left").replace('px', ''))
                +Number(child.css("padding-left").replace('px', ''))
                +Number(child.get(0).style.borderLeftWidth.replace('px', ''))
                +Number(child.css("margin-right").replace('px', ''))
                +Number(child.css("padding-right").replace('px', ''))
                +Number(child.get(0).style.borderRightWidth.replace('px', ''));
        _h += Number(child.css("margin-top").replace('px', ''))
                +Number(child.css("padding-top").replace('px', ''))
                +Number(child.get(0).style.borderTopWidth.replace('px', ''))
                +Number(child.css("margin-bottom").replace('px', ''))
                +Number(child.css("padding-bottom").replace('px', ''))
                +Number(child.get(0).style.borderBottomWidth.replace('px', ''));
        return [_w, _h];
    };
    function heightTo(self)
    {
        var _self = self;
        var _delay = _self.data("_vgchild").length
            * (_self.data("_vgopt").delay || 0)
            + _self.data("_vgopt").time || 500;
        _self.stop();
        if(_self.height() < _self.data("_vgwrapheight"))
        {
            if($.browser.msie)
            {
                _self.height(_self.data("_vgwrapheight"));
            }
            else
            {
                _self.animate(
                    {
                        height: _self.data("_vgwrapheight")+"px"
                    },
                    (_self.data("_vgopt").time || 500),
                    "easeOutQuart"
                );
            }
        }
        else
        {
            clearTimeout(_self.data("_vgwraptimeout"));
            _self.data("_vgwraptimeout", setTimeout(function(){
                if($.browser.msie)
                {
                    _self.height(_self.data("_vgwrapheight"));
                }
                else
                {
                    _self.animate(
                        {
                            height: _self.data("_vgwrapheight")+"px"
                        },
                        (_self.data("_vgopt").time || 500),
                        "easeOutQuart"
                    );
                }
            }, _delay));
        }
    };
    function moveTo(childs)
    {
        var _c;
        childs.each(function(i)
        {
            _c = $(this);
            _c.css("left", ~~_c.data("_vgleft")+"px");
            _c.css("top", ~~_c.data("_vgtop")+"px");
        });
    };
    function animateTo(childs, easing, time, delay)
    {
        var _self = $(childs).parent();
        var isMove = false;
        var imax = childs.length;
        var i,_c,_pos;
        for(i=0; i<imax; i++)
        {
            _c = $(childs[i]);
            _pos = _c.position();
            if(_pos.left != _c.data("_vgleft") && _pos.top != _c.data("_vgtop"))
            {
                isMove = true;
            }
        }
        if(isMove)
        {
            if(typeof(_self.data("_vgopt").onStart) == "function") _self.data("_vgopt").onStart();
            childs.each(function(i)
            {
                var _c = $(this);
                var _opt = {
                    duration: time,
                    easing: easing
                };
                if(childs.size()-1 == i)
                {
                    _opt.complete = _self.data("_vgopt").onFinish || null;
                }
                clearTimeout(_c.data("_vgtimeout"));
                _c.data("_vgtimeout", setTimeout(function(){
                    _c.animate(
                        {
                            left: _c.data("_vgleft")+"px",
                            top: _c.data("_vgtop")+"px"
                        },
                        _opt
                    );
                }, i*delay));
            });
        }
    };
    function jumpTo(childs)
    {
        var _self = $(childs).parent();
        var isMove = false;
        var imax = childs.length;
        var i,_c,_pos;
        for(i=0; i<imax; i++)
        {
            _c = $(childs[i]);
            _pos = _c.position();
            if(_pos.left != _c.data("_vgleft") && _pos.top != _c.data("_vgtop"))
            {
                isMove = true;
            }
        }
        if(isMove)
        {
            if(typeof(_self.data("_vgopt").onStart) == "function") _self.data("_vgopt").onStart();
            childs.each(function(i)
            {
                var _c = $(this);
                clearTimeout(_c.data("_vgtimeout"));
                    _c.css(
                        {
                            left: _c.data("_vgleft")+"px",
                            top: _c.data("_vgtop")+"px"
                        }
                    );
            });
        }
    };
    
    function refleshHandler(tg)
    {
        var _self = tg;
        clearTimeout(_self.data("_vgtimeout"));
        makePos(_self);
        _self.data("_vgtimeout", setTimeout(function(){
            animateTo(
                _self.data("_vgchild"),
                _self.data("_vgopt").easeing || "linear",
                _self.data("_vgopt").time || 500,
                _self.data("_vgopt").delay || 0
            );
        }, 500));
    };
    function setFontSizeListener(self, func)
    {
        var s = $("<span />")
            .text(" ")
            .attr("id", "_vgridspan")
            .hide()
            .appendTo("body");
        s.data("size", s.css("font-size"));
        s.data("timer", setInterval(function(){
            if(s.css("font-size") != s.data("size"))
            {
                s.data("size", s.css("font-size"));
                func(self);
            }
        }, 1000));
    };
    function setImgLoadEvent(self, func)
    {
        self.bind("vgrid-added", function(){
            self.find("img").bind("load", function(){
                var _self = self;
                makePos(_self);
                jumpTo(
                    _self.data("_vgchild")
                );
            });
        });
        self.trigger("vgrid-added");
        var _append = self.append;
        var _prepend = self.prepend;
        self.append = function(){
            _append.apply(self, arguments);
            self.trigger("vgrid-added");
        };
        self.prepend = function(){
            _prepend.apply(self, arguments);
            self.trigger("vgrid-added");
        };
    };
    $.fn.extend({
        vgrid: function(option)
        {
            var _self = $(this);
            var _opt = option || {};
            _self.data("_vgopt", _opt);
            _self.data("_vgchild", _self.find("> *"));
            _self.data("_vgdefchild", _self.data("_vgchild"));
            _self.css({
                "position": "relative",
                "width": "auto"
            });
            _self.data("_vgchild").css("position", "absolute");
            makePos(_self);
            moveTo(_self.data("_vgchild"));
            if(_self.data("_vgopt").fadeIn)
            {
                var _prop = (typeof(_self.data("_vgopt").fadeIn)=='object')
                                ? _self.data("_vgopt").fadeIn
                                : {time: _self.data("_vgopt").fadeIn} ;
                _self.data("_vgchild").each(function(i)
                {
                    var _c = $(this);
                    _c.css('display', 'none');
                    setTimeout(function(){
                        _c.fadeIn(_prop.time || 250);
                    }, i * (_prop.delay || 0));
                });
            }
            $(window).resize(function(e)
            {
                refleshHandler(_self);
            });
            if(_opt.useLoadImageEvent) setImgLoadEvent(_self, refleshHandler);
            if(_opt.useFontSizeListener) setFontSizeListener(_self, refleshHandler);
            return _self;
        },
        vgrefresh: function(easeing, time, delay, func)
        {
            var _obj = $(this);
            if(_obj.data("_vgchild"))
            {
                _obj.data("_vgchild", _obj.find("> *"));
                _obj.data("_vgchild").css("position", "absolute");
                makePos(_obj);
                time = typeof(time)=="number" ? time : _obj.data("_vgopt").time || 500;
                delay = typeof(delay)=="number" ? delay : _obj.data("_vgopt").delay || 0;
                animateTo(
                    _obj.data("_vgchild"),
                    easeing || _obj.data("_vgopt").easeing || "linear",
                    time,
                    delay
                );
                if(typeof(func)=='function')
                {
                    setTimeout(
                        func,
                        _obj.data("_vgchild").length * delay + time
                    );
                }
            }
            return _obj;
        },
        vgsort: function(func, easeing, time, delay)
        {
            var _obj = $(this);
            if(_obj.data("_vgchild"))
            {
                _obj.data("_vgchild", _obj.data("_vgchild").sort(func));
                _obj.data("_vgchild").each(function(num){
                    $(this).appendTo(_obj);
                });
                makePos(_obj);
                animateTo(
                    _obj.data("_vgchild"),
                    easeing || _obj.data("_vgopt").easeing || "linear",
                    typeof(time)=="number" ? time : _obj.data("_vgopt").time || 500,
                    typeof(delay)=="number" ? delay : _obj.data("_vgopt").delay || 0
                );
            }
            return _obj;
        }
    });
})(jQuery);

