/*************************************************
*
* $Id: transition.js 9 2009-02-16 15:20:36Z yalpedoc $
* $Author: yalpedoc $
* $Date: 2009-02-16 23:20:36 +0800 (Mon, 16 Feb 2009) $
* $Rev: 9 $
*
**************************************************/
var codeplay;
if (!codeplay) codeplay = {};  
if (!codeplay.common) codeplay.common = function(){};
if (!codeplay.image) codeplay.image = function(){}; 

if (!codeplay.image.TransitionEffectBase) codeplay.image.TransitionEffectBase = function(){ 
	codeplay.common.Utils.log("In TransitionEffectBase()");
};

codeplay.image.TransitionEffectBase.prototype = {
    effectViewTime: 1500, //Effect view time, default is 1500ms, could be changed by client code
    userAgent: navigator.userAgent,
    userAgentLowerCase: navigator.userAgent.toLowerCase(),
    initBase: function(mgr) {
        codeplay.common.Utils.log("In TransitionEffectBase.initBase()");
        this.transitionMgr = mgr;
        this.imgHeight = this.transitionMgr.imgHeight;
        this.imgWidth = this.transitionMgr.imgWidth;
        this.x = this.transitionMgr.x;
        this.y = this.transitionMgr.y;
        this.currentImg = null;
        this.nextImg = null;
        //following are all default values, could be modified in child class's constructor "init()" freely
        this.fps = 400;
        this.interval = Math.round(1000.0 / this.fps);
        this.effTimerId = null;
        this.pauseViewTimerId = null;
    },
    loop: function() {
        if (!this.terminate) {
            //following few params might be useful if one wanna write time sensitive effect
            this.currentTime = codeplay.common.Utils.getCurrentMS();
            this.totalElapsedMS = this.currentTime - this.startTime;
            this.frameTime = this.currentTime - this.previousTime;
            this.update();
            this.previousTime = this.currentTime;
            ++this.frame;
            this.effTimerId = codeplay.common.Utils.setTimeout(this.loop, this.interval, this);
        } else if (this.effTimerId != null) {
            clearTimeout(this.effTimerId); this.effTimerId = null;
        }
    },
    start: function() {
        codeplay.common.Utils.log("In TransitionEffectBase.start()");
        if (this.transitionMgr.stopped) return;
        if (this.pauseViewTimerId != null) { clearTimeout(this.pauseViewTimerId); this.pauseViewTimerId = null; }
        if (this.effTimerId != null) { clearTimeout(this.effTimerId); this.effTimerId = null; }
        this.terminate = false;
        this.frame = 0;
        this.previousTime = codeplay.common.Utils.getCurrentMS();
        this.startTime = this.previousTime;
        this.totalElapsedMS = 0;
        this.frameTime = 0;
        this.transitionMgr.displayImgChanged(this.transitionMgr.nextImgId);
        this.onStart();
        this.loop();
        //this.timerId = codeplay.common.Utils.setInterval(this.loop, this.interval,this);
    },
    stop: function() {
        codeplay.common.Utils.log("In TransitionEffectBase.stop()");
        if (this.pauseViewTimerId != null) { clearTimeout(this.pauseViewTimerId); this.pauseViewTimerId = null; }
        if (this.effTimerId != null) { clearTimeout(this.effTimerId); this.effTimerId = null; }
        if (this.terminate) return;
        this.terminate = true;
        //if (this.timerId) clearInterval(this.timerId);
        this.onStop();
        this.transitionMgr.transitionEnd();
    },
    /**
    * Should be implemented by the child class, return true if current effect is supported by browser; false otherwise
    */
    isCurrentBrowserSupported: function() {
        return true;
    },
    update: function() {
        throw "Function update() of TransitionEffectBase must be override by child class";
    },
    /**
    * Child class should overwrite this, this method will display the current image, at the end of the transition, the
    * mgr will call this method to let the next transition effect take over the resposibility of showing the img
    */
    displayImg: function(imgId) {
        throw "Function displayCurrentImg() of TransitionEffectBase must be override by child class";
    },
    /**
    * Child class should overwrite this, this method will hide the image displayed by function "displayImg(imgId)", 
    * at the end of the transition, the mgr will call this method to let the hide the image so that next transition effect
    * could take over the resposibility
    */
    hideImg: function() {
        throw "Function displayCurrentImg() of TransitionEffectBase must be override by child class";
    },
    /**
    * To be overwrite by child class if needed
    */
    onStart: function() { },
    /**
    * To be overwrite by child class if needed
    */
    onStop: function() { },
    userAgent: navigator.userAgent,
    userAgentLowerCase: navigator.userAgent.toLowerCase(),
    pauseForUserView: function() {
		codeplay.common.Utils.log("In TransitionEffectBase.pauseForUserView()");
        if(this.effTimerId != null) { clearTimeout(this.effTimerId); this.effTimerId = null; }
        if(this.pauseViewTimerId!=null) {
            clearTimeout(this.pauseViewTimerId);
            this.pauseViewTimerId = null;
        }
        this.pauseViewTimerId = codeplay.common.Utils.setTimeout(this.stop, this.effectViewTime, this);
    }
};  //end of codeplay.image.TransitionEffectBase

/**
* Manages the images transitions
* @param interval The interval in milli second between the previous transition end time and current transition start time
* @param effects Array of effects to be used
*/
codeplay.image.TransitionMgr = function(interval,position_x,position_y,imageWidth, imageHeight,enableURL){
    this.init(interval, position_x, position_y, imageWidth, imageHeight, enableURL);
};

codeplay.image.TransitionMgr.prototype = {
    start: function() {
        codeplay.common.Utils.log("In TransitionMgr.start()");
        if (this.stopped == false) return false;
        if (this.timerId != null) return false;
        this.effectId = 0;
        this.currentImgId = 0;
        return this.resume();
    },
    transitionEnd: function() {
        codeplay.common.Utils.log("In TransitionMgr.transitionEnd(), currentImgId:" + this.currentImgId);
        if (this.timerId != null) { clearTimeout(this.timerId); this.timerId = null; };
        if (this.stopped) return;
        this.currentImgId = (this.currentImgId + 1) % this.images.length;
        this.nextImgId = (this.currentImgId + 1) % this.images.length;
        this.nextEffect = null;
        this.currentEffect = this.transitionEffects[this.effectId];
        this.preEffectId = this.effectId;
        while (this.stopped == false) {
            this.effectId = (this.effectId + 1) % this.transitionEffects.length;
            this.nextEffect = this.transitionEffects[this.effectId];
            if (this.nextEffect.isCurrentBrowserSupported()) {
                this.nextEffect.displayImg(this.currentImgId);
                this.setImageUrl(this.nextImgId);
                if (this.effectId != this.preEffectId)
                    this.currentEffect.hideImg();
                //if (!this.stopped ) this.timerId = codeplay.common.Utils.setTimeout(this.nextEffect.start, this.interval, this.nextEffect);
                if (!this.stopped) this.nextEffect.start();
                break;
            }
        }
    },
    stop: function() {
        codeplay.common.Utils.log("In TransitionMgr.stop()");
        if (this.stopped) return;
        this.stopped = true;
        if (this.timerId != null) { clearTimeout(this.timerId); this.timerId = null; }
        //if (this.nextEffect) this.nextEffect.stop();
        //if (this.currentEffect) this.currentEffect.stop();
        //this.transitionEffects[this.effectId].stop();
        for (var i = 0; i < this.transitionEffects.length; ++i)
            if (this.transitionEffects[i].isCurrentBrowserSupported())
                this.transitionEffects[i].stop();
    },
    /**
    * Display the specified image, usually called after stopping the mgr
    */
    displayImg: function(imgId) {
        codeplay.common.Utils.log("In TransitionMgr.displayImg(), currentImgId: " + this.currentImgId + "; imgId: " + imgId + "; effectId: " + this.effectId);
        /*
        //TODO: testing, improve following
        //if(this.currentImgId == imgId) return;
        //if(this.currentEffect) this.currentEffect.hideImg();
        for (var i = 0; i < this.transitionEffects.length; ++i)
        if (this.transitionEffects[i].isCurrentBrowserSupported())
        this.transitionEffects[i].hideImg();
        this.currentImgId = imgId;
        this.transitionEffects[this.effectId].displayImg(this.currentImgId);
        this.setImageUrl(this.currentImgId);
        */
        if (this.currentImgId == imgId) return;
        this.currentImgId = imgId;
        this.staticImgDiv.style.width = this.imgWidth + 'px';
        this.staticImgDiv.style.backgroundImage = 'url(' + this.images[this.currentImgId].src + ')';
        this.setImageUrl(this.currentImgId);
        for (var i = 0; i < this.transitionEffects.length; ++i)
            if (this.transitionEffects[i].isCurrentBrowserSupported())
                this.transitionEffects[i].hideImg();
    },
    resume: function() {
        codeplay.common.Utils.log("In TransitionMgr.resume()");
        this.staticImgDiv.style.backgroundImage = '';
        this.staticImgDiv.style.width = '0px';
        var count = 0;
        if (!this.stopped) return;
        if (this.timerId != null) { clearTimeout(this.timerId); this.timerId = null; };
        //this.currentImgId = (this.currentImgId + 1) % this.images.length;
        this.nextImgId = (this.currentImgId + 1) % this.images.length;
        this.setImageUrl(this.nextImgId);
        while (count < this.transitionEffects.length) {
            this.effectId = (this.effectId + 1) % this.transitionEffects.length;
            this.nextEffect = this.transitionEffects[this.effectId];
            if (this.nextEffect.isCurrentBrowserSupported()) {
                this.stopped = false;
                this.nextEffect.displayImg(this.currentImgId);
                this.nextEffect.start();
                //if (!this.stopped) this.timerId = codeplay.common.Utils.setTimeout(this.nextEffect.start, this.interval, this.nextEffect);
                //this.displayImgChanged(this.nextImgId);
                break;
            }
            ++count;
        }
        return !this.stopped;
    },
    /**
    * Constructor
    */
    init: function(interval, position_x, position_y, imageWidth, imageHeight, _enableURL) {
        this.stopped = true;
        this.timerId = null;
        this.interval = interval;
        this.transitionEffects = new Array(0);
        this.effectId = 0;
        this.titleHeight = 15;
        this.x = 0;
        this.y = 0;
        this.imgHeight = imageHeight;
        this.imgWidth = imageWidth;
        this.enableURL = _enableURL;
        this.titleDivBackgroundColor = '#FFFFCC';
        this.titleFontFamily = 'Arial, Helvetica, sans-serif';
        this.timerId = null;
        this.displayImgChangedEvent = 'imagechange';
        do {
            this.imagesDivId = "imagesDiv" + codeplay.common.Utils.getCurrentMS() + Math.round(Math.random() * 100);
        } while (document.getElementById(this.imagesDivId));
        this.currentImgId = 0;
        this.images = new Array(0);
        this.imageUrls = new Array(0);
        this.imagesDiv = document.createElement('div');
        this.staticImgDiv = document.createElement('div');
        if (this.enableURL) {
            this.imageURL = document.createElement('a');
            this.imageURL = document.body.appendChild(this.imageURL);
            this.imagesDiv = this.imageURL.appendChild(this.imagesDiv);
            this.staticImgDiv = this.imageURL.appendChild(this.staticImgDiv);
            this.imageURL.style.position = 'absolute';
            this.imageURL.style.left = position_x + 'px';
            this.imageURL.style.top = position_y + 'px';
            this.imageURL.style.width = this.imgWidth + 'px';
            this.imageURL.style.height = (this.imgHeight + this.titleHeight) + 'px';
            this.imageURL.style.textDecoration = 'none';
            this.titleDiv = document.createElement('div');
            this.titleDiv = this.imageURL.appendChild(this.titleDiv);
            this.titleDiv.style.position = 'absolute';
            this.titleDiv.style.left = '0px';
            this.titleDiv.style.top = this.imgHeight + 'px';
            this.titleDiv.style.width = this.imgWidth + 'px';
            this.titleDiv.style.height = this.titleHeight + 'px';
            this.titleDiv.style.textAlign = 'center';
            this.titleDiv.style.backgroundColor = this.titleDivBackgroundColor;
            this.titleDiv.style.fontSize = this.titleHeight + 'px';
            this.titleDiv.style.fontFamily = this.titleFontFamily;
        } else {
            this.imagesDiv = document.body.appendChild(this.imagesDiv);
            this.staticImgDiv = document.body.appendChild(this.staticImgDiv);
        }
        this.staticImgDiv.style.position = 'absolute';
        this.imagesDiv.id = this.imagesDivId;
        this.imagesDiv.style.position = 'absolute';
        if (_enableURL) {
            this.imagesDiv.style.left = this.x + 'px';
            this.imagesDiv.style.top = this.y + 'px';
            this.staticImgDiv.style.left = this.x + 'px';
            this.staticImgDiv.style.top = this.y + 'px';
        } else {
            this.imagesDiv.style.left = position_x + 'px';
            this.imagesDiv.style.top = position_y + 'px';
            this.staticImgDiv.style.left = position_x + 'px';
            this.staticImgDiv.style.top = position_y + 'px';
        }
        this.imagesDiv.style.width = this.imgWidth + 'px';
        this.imagesDiv.style.height = this.imgHeight + 'px';
        this.staticImgDiv.style.width = '0px';
        this.staticImgDiv.style.height = this.imgHeight + 'px';
        this.staticImgDiv.style.zIndex = 10;
        this.currentEffect = null;
        this.nextEffect = null;
        this.preEffectId = 0;
    },
    resizeDiv: function(x, y, width, height) {
        this.x = x; this.y = y; this.imgWidth = width; this.imgHeight = height;
        this.imagesDiv.style.left = this.x + 'px';
        this.imagesDiv.style.top = this.y + 'px';
        this.imagesDiv.style.width = this.imgWidth + 'px';
        this.imagesDiv.style.height = this.imgHeight + 'px';
    },
    addEffects: function(effects) {
        for (var i in effects) this.transitionEffects.push(effects[i]);
    },
    addImages: function(imgs) {
        for (var i in imgs) this.images.push(imgs[i]);
    },
    addImageUrls: function(imgUrls) {
        for (var i in imgUrls) this.imageUrls.push(imgUrls[i]);
    },
    setImageUrl: function(imgId) {
        if (this.enableURL && this.imageURL && this.titleDiv && this.imageUrls && this.imageUrls.length > imgId) {
            var tmp = 0;
            this.imageURL.href = this.imageUrls[imgId][tmp++];
            this.imageURL.target = this.imageUrls[imgId][tmp++];
            this.imageURL.title = this.imageUrls[imgId][tmp++];
            this.titleDiv.innerHTML = this.imageUrls[imgId][tmp];
        }
    },
    /*
    * Only fired for natural transition, not fired when user manually changed image
    */
    displayImgChanged: function(imgId) {
        codeplay.common.Utils.log("In TransitionMgr.displayImgChanged(), imgId:" + imgId);
        if (this.imagesDiv && !this.stopped) {
            var e;
            if (!codeplay.common.Utils.isIE()) {
                e = document.createEvent('Events');
                e.initEvent(this.displayImgChangedEvent, false, false);
                e.imgId = imgId;
                this.imagesDiv.dispatchEvent(e);
            } else {//IE
                e = document.createEventObject();
                e.imgId = imgId;
                //this.imagesDiv.fireEvent("onimagechange", e);//TODO
                if (this.imagesDiv['on' + this.displayImgChangedEvent])
                    this.imagesDiv['on' + this.displayImgChangedEvent](e);
            }
        }
    }
};                       //end of codeplay.image.TransitionMgr.prototype


/**
* Commonly used methods
*/
codeplay.common.Utils = function() {
    return {
        /**
        * Bind event to element
        */
        addEvent: function(element, eventType, func, capture) {
            if (element.addEventListener) {
                element.addEventListener(eventType, func, capture);
                return true;
            }/*
            else if (element.attachEvent) {
                var r = element.attachEvent('on' + eventType, func);
                //element['on' + eventType] = func;
                return r;
            }*/
            else {
                element['on' + eventType] = func;
            }
        },
        /**
        * Write log messages to supported browser, eg: google chrome
        */
        log: function(i) { if (window.console && window.console.log) window.console.log(i); },
        /**
        * Check if parameter is defined
        */
        isset: function(a) { return typeof a != 'undefined'; },
        getCurrentMS: function() { return (new Date()).getTime(); },
        getEffect: function(eff, mgr) {
            eff.initBase(mgr);
            if (eff.init) eff.init();
            return eff;
        },
        /**
        * Cross platform setTimeout (for functions without argument verson), 
        * will provide with argument version in the future if needed
        */
        setTimeout: function(f, t, s) {
            if (!document.all) //TODO: change here
                return window.setTimeout(function() { f.apply(s); }, t);
            else {//IE
                return window.setTimeout(function() { f.apply(s); }, t);
            }
        },
        /**
        * Cross platform setInterval (for functions without argument verson), 
        * will provide with argument version in the future if needed
        */
        setInterval: function(f, t, s) {
            if (!document.all) { //TODO: change here
                return window.setInterval(function() { f.apply(s); }, t);
            } else {//IE
                return window.setInterval(function() { f.apply(s); }, t);
            }
        },
        isIE: function() {
            if (document.all) return true;
            return false;
        },
        getElementsByTagName: function(element, tagName, greedy) {
            var ret = new Array();
            if (element == null || !element) return ret;
            tagName = tagName.toLowerCase();
            var allNodes = new Array();
            allNodes.push(element);
            while (allNodes.length > 0) {
                var n = allNodes.pop();
                if (n.tagName && n.tagName.toLowerCase() == tagName) {
                    ret.push(n);
                    if (greedy) break;
                }
                var children = n.childNodes;
                if (children) for (var i in children) allNodes.push(children[i]);
            }
            allNodes = null;
            return ret;
        },
        setOpacity: function(element, percentage) {
            if (codeplay.common.Utils.isIE()) {
                element.style.filter = 'alpha(opacity=' + percentage + ')';
            } else {
                element.style.opacity = percentage / 100.0;
            }
        }
    }
} ();

