window.extend({
    fixDocHeight: function() {
        // fix the document height, so sliding in and out within
        // the <body> don't make the scrollbar judder up with it
        var de = document.documentElement;
        de.style.height = de.offsetHeight + 'px';
    },
    
    // release the fix on the document height
    unfixDocHeight: function() {
        document.documentElement.style.height = '';
    }
});

Element.extend({
    vanish: function(opt) {
        var c = new Chain();
        var el = this;
        
        // calculate current height by adding the 'actual'
        // height to the vertical margins. this allows us
        // to roll-up the amount of space that this element
        // occupies in the document, to avoid a margin-sized
        // judder at the end of the transition, when the
        // element is removed from the dom
        this.full_height = this.offsetHeight +
            this.getStyle('margin-top').toInt() +
            this.getStyle('margin-bottom').toInt();
        
        // fade out the object, then slide
        // the remaining box out of view
        var s = new Fx.Style( this, 'height',  opt );
        var f = new Fx.Style( this, 'opacity', opt );
        f.start(0).chain(function() {
            
            // set the height of the element absolutely, so
            // it includes the top/bottom margins, and removes
            // them. this prepares a chunk for rollup, without
            // jerking as the margins are removed at the end
            el.setStyles({
                height: el.full_height + 'px',
                overflow: 'hidden',
                'min-height': 0,
                'margin-top': 0,
                'margin-bottom': 0,
                opacity: 0
            });
            
            // roll-up the element to zero height, then
            // unlock the document height and call any
            // events chained to this (Element.vanish)
            s.start(0).chain(function() { c.callChain() });
        });
        
        return c;
    },
    
    unvanish: function(opt) {
        var c = new Chain();
        var el = this;
        
        // unroll the element to its original, full,
        // height, and fade it back in to 100% opaque
        var s = new Fx.Style( this, 'height',  opt );
        var f = new Fx.Style( this, 'opacity', opt );
        s.start(this.full_height).chain(function() {
        
            // remove the overrides that were added
            // to the element when it vanished
            el.setStyles({
                height: '',
                overflow: '',
                'min-height': '',
                'margin-top': '',
                'margin-bottom': '',
                opacity: ''
            });
            
            f.start(1).chain(function() { c.callChain() });
        });
        
        return c;
    }
});
