/* 
 This file was generated by Dashcode and is covered by the 
 license.txt included in the project.  You may edit this file, 
 however it is recommended to first turn off the Dashcode 
 code generator otherwise the changes will be lost.
 */

// Note: Properties and methods beginning with underbar ("_") are considered private and subject to change in future Dashcode releases.

// Currently supported transition types
Transition.NONE_TYPE = 'none';
Transition.PUSH_TYPE = 'push';
Transition.DISSOLVE_TYPE = 'dissolve';
Transition.SLIDE_TYPE = 'slide';
Transition.FADE_TYPE = 'fade';

// Transition timing functions that are defined as part of WebKit CSS animation specification. These are made available for your convenience.
Transition.EASE_TIMING = 'ease';
Transition.LINEAR_TIMING = 'linear';
Transition.EASE_IN_TIMING = 'ease-in';
Transition.EASE_OUT_TIMING = 'ease-out';
Transition.EASE_IN_OUT_TIMING = 'ease-in-out';

// These are extra properties for Transition.PUSH_TYPE
Transition.RIGHT_TO_LEFT_DIRECTION = 'right-left';
Transition.LEFT_TO_RIGHT_DIRECTION = 'left-right';
Transition.TOP_TO_BOTTOM_DIRECTION = 'top-bottom';
Transition.BOTTOM_TO_TOP_DIRECTION = 'bottom-top';

//
// Constructor for Transtition object. You can also use the convenience method CreateTransitionWithProperties()
//
// type         - any of the Transition type constants
// duration     - a float in seconds
// timing       - a valid CSS animation timing function value. For example, 'linear' or 'ease-in-out'
//
function Transition(type, duration, timing)
{
    this.type = type;
    this.duration = duration;
    this.timing = timing;
    
    this._useTransforms = window.WebKitCSSMatrix ? true : false;
}

//
// Create a new Transition object and fill its internal properties from the dictionary parameter
//
function CreateTransitionWithProperties(properties)
{
    var transition = new Transition();
    for (var property in properties) {
        transition[property] = properties[property];
    }
    return transition;
}

//
// Both newView and oldView must share the same common parent container element. The transition is constrained by
// the dimensions of the parent container. In particular, the container has 'overflow: hidden'. This is especially
// important when the container edges are not lined with the edge of the device viewport.
//
// isReverse - if flag is true, it will perform the transition in reverse. Some transitions, for example, the push transition has a reverse.
//
Transition.prototype.perform = function(newView, oldView, isReverse)
{
    var containerElement = oldView ? oldView.parentNode : (newView ? newView.parentNode : null);
    if (!containerElement) return;
    
    if (oldView && newView) {
        // Got to execute in the same container
        if (oldView.parentNode != newView.parentNode) return;
		if (oldView == newView) return;
    }
    
    // Make sure that container is constraining the transitions for overflow content
    containerElement.style.overflow = 'hidden';
    var computedStyle = document.defaultView.getComputedStyle(containerElement, null);
    if ((computedStyle.getPropertyValue('position') != 'absolute') && (computedStyle.getPropertyValue('position') != 'relative')) {
        // Assume 'static' since we don't support 'fixed'. 'relative' is less obtrusive then.
        containerElement.style.position = 'relative';
    }
    
    var newStyle = newView.style;
    if (oldView) {
        var oldStyle = oldView.style;
        // Reset
        oldStyle.zIndex = 0;
        oldStyle.position = 'absolute';
        // Since oldView is just taken out of the document flow, make sure its width still looks good
        oldStyle.width = containerElement.offsetWidth+'px';
        // This is especially important for reverse since the original value of 'top' is 'auto', which for a view lower in the document flow means that it will come after the newly restored view. Hence, let's set it to a right value before newView is put to 'relative' again.
        oldStyle.top = oldView.offsetTop + 'px';
        
        if (this.type && this.type != 'none' && this._useTransforms) {
            // This makes sure that we start with an identity matrix to avoid initial performance problem
            if (!oldStyle.webkitTransform) {
                oldStyle.webkitTransform = 'translate(0px, 0px)';
            }
            if (!newStyle.webkitTransform) {
                newStyle.webkitTransform = 'translate(0px, 0px)';
            }

            if (this.type == Transition.FADE_TYPE) {
                if (!isReverse) newStyle.opacity = 0;
            }
            else if (this.type == Transition.DISSOLVE_TYPE) {
                newStyle.opacity = 0;
            }
        }
        else {
            oldStyle.opacity = 0;
        }
    }
    
    // Performing the transition now
    if (this.type && this.type != 'none' && this._useTransforms) {
        var self = this;
        if (this.type == Transition.DISSOLVE_TYPE || this.type == Transition.FADE_TYPE) {
            if (oldView) {
                var parsedDuration = this._parseDurationForFadingEffect(this.duration);
                Transition._addDelayedTransitionCallback(function() {
                    if (self.type == Transition.DISSOLVE_TYPE || (self.type == Transition.FADE_TYPE && isReverse)) {
                        oldStyle.webkitTransitionProperty = 'opacity';
                        oldStyle.webkitTransitionDuration = parsedDuration;
                        oldStyle.webkitTransitionTimingFunction = self.timing;
                        oldStyle.opacity = 0;
                        
                        if (self.type == Transition.FADE_TYPE && isReverse) {
                            // Need to set the newView's zIndex to be 1, especially for reversing
                            var duration = parseFloat(self.duration);
                            var timerCallback = function() {
                                newStyle.zIndex = 1;
                            }
                            setTimeout(timerCallback, duration*1000);
                        }
                    }

                    if (self.type == Transition.DISSOLVE_TYPE || (self.type == Transition.FADE_TYPE && !isReverse)) {
                        newStyle.webkitTransitionProperty = 'opacity';
                        newStyle.webkitTransitionDuration = parsedDuration;
                        newStyle.webkitTransitionTimingFunction = self.timing;
                        newStyle.opacity = null;
                    }
                });
            }
            else {
                newStyle.opacity = null;
            }
        }
        else if (this.type == Transition.PUSH_TYPE || this.type == Transition.SLIDE_TYPE) {
            var transformOp = 'translateX';
            if (oldView) {
                var factor = isReverse ? -1 : 1;
                var dimension = containerElement.offsetWidth;
                if (this.direction == 'bottom-top') {
                    transformOp = 'translateY';
                    dimension = isReverse ? newView.offsetHeight : oldView.offsetHeight;
                } else if (this.direction == 'top-bottom') {
                    transformOp = 'translateY';
                    dimension = isReverse ? oldView.offsetHeight : newView.offsetHeight;
                }
                if (this.direction == 'left-right' || this.direction == 'top-bottom') factor *= -1;
                
                if (this.type == Transition.PUSH_TYPE || (this.type == Transition.SLIDE_TYPE && !isReverse)) {
                    this._disableTransitions(newStyle);
                    newStyle.webkitTransform = transformOp + '(' + factor*dimension + 'px)';
                }
                
                Transition._addDelayedTransitionCallback(function() {
                    var parsedDuration = self._parseDuration(self.duration);
                    
                    if (self.type == Transition.PUSH_TYPE || (self.type == Transition.SLIDE_TYPE && isReverse)) {
                        oldStyle.webkitTransitionProperty = '-webkit-transform';
                        oldStyle.webkitTransitionDuration = parsedDuration;
                        oldStyle.webkitTransitionTimingFunction = self.timing;
                        // +1 to make it different from previous value, else it will not show up in bottom-top
                        oldStyle.webkitTransform = transformOp + '(' + -1*factor*(dimension+1) + 'px)';
                        
                        if (self.type == Transition.SLIDE_TYPE && isReverse) {
                            // Need to set the newView's zIndex to be 1, especially for reversing
                            var duration = parseFloat(self.duration);
                            var timerCallback = function() {
                                newStyle.zIndex = 1;
                            }
                            setTimeout(timerCallback, duration*1000);
                        }
                    }
                    
                    if (self.type == Transition.PUSH_TYPE || (self.type == Transition.SLIDE_TYPE && !isReverse)) {
                        newStyle.webkitTransitionProperty = '-webkit-transform';
                        newStyle.webkitTransitionDuration = parsedDuration;
                        newStyle.webkitTransitionTimingFunction = self.timing;
                        newStyle.webkitTransform = 'translate(0px, 0px)';
                    }
                    
                    if (self.type == Transition.PUSH_TYPE || (self.type == Transition.SLIDE_TYPE && isReverse)) {
                        // This is a workaround for <rdar://5667580>. It will be removed when the bug is fixed. Currently, I need this workaround, especially for bottom-top transition, else the old view will end up covering the header (the workaround doesn't work well for opacity).
                        // Doing this in the callback function because in hardware device, the transition setup is so slow that by the time the transition happens, oldView already has 'opacity: 0';
                        var duration = parseFloat(self.duration);
                        var timerCallback = function() {
                            oldStyle.opacity = 0;
                        }
                        setTimeout(timerCallback, duration*1000+10);
                    }
                });
            }
            else {
                oldStyle.opacity = 0;
            }
        }

        var originalContainerElementHeight = containerElement.style.height;
        containerElement.style.height = Math.max(oldView.offsetHeight, newView.offsetHeight)+'px';
        var duration = parseFloat(this.duration);
        var timerCallback = function() {
            containerElement.style.height = originalContainerElementHeight;
        }
        setTimeout(timerCallback, duration*1000);
    }
    
    if ((this.type != Transition.FADE_TYPE && this.type != Transition.SLIDE_TYPE) || !this._useTransforms) {
        // In the reverse transtion for Transition.FADE_TYPE, newStyle.zIndex needs to be set to 1 after the oldStyle has been faded out, else you will not see the transition
        newStyle.zIndex = 1;
    }
    // Before the new view comes in, remove any previously hard-coded inline value that would have crept in when it was transited out. Because the transition is happening in a container and we are reinstating the position to be 'relative', the new view will resize itself to react to orientation changes (if any).
    newStyle.width = null;
    newStyle.position = 'relative';
}

Transition.prototype._parseDuration = function(duration)
{
    var value = parseFloat(duration);
    if (!isNaN(value)) {
        value += 's';
    }
    else {
        value = '0s';
    }
    return value;
}

Transition.prototype._parseDurationForFadingEffect = function(duration)
{
    var value = parseFloat(duration);
    if (!isNaN(value)) {
        // looks better with slightly longer timing
        value = value * (1+ ((value < 0.25) ? 0.5 : Math.pow(4, -0.25-value))) + 's';
    }
    else {
        value = '0s';
    }
    return value;
}

Transition.prototype._disableTransitions = function(style)
{
    style.webkitTransitionProperty = 'none';
    style.webkitTransitionDuration = '0';
    style.webkitTransitionTimingFunction = '';
}

// Accumulate transitions that will be executed after a 0 delay
Transition._addDelayedTransitionCallback = function(callback) {
	if (!Transition._delayedCallbacks) {
		Transition._delayedCallbacks = new Array();
		var performDelayedCallbacks = function () {
            var length = Transition._delayedCallbacks.length;
			for (var f=0; f<length; f++) {
				Transition._delayedCallbacks[f]();
			}
			delete Transition._delayedCallbacks;
		}
		setTimeout(performDelayedCallbacks, 0);
	}
	Transition._delayedCallbacks.push(callback);
}

