/* -------------------------------------------------------------------------- *
 *   Experience Javascript Library (version 1.0b_jQuery)
 *  (c) 2007 Ahmed Saad <ahmeds@users.sourceforge.net>
 *
 *  Experience is freely distributable under the terms of an MIT-style license.
 *  For details, see project website at http://experience.sf.net
 * -------------------------------------------------------------------------- */

// Panorama component
experience.panorama  =  {

    Version : '0.2',

    //// MNEMONICS ////////////////////////////////////////////

    ZOOM_IN : 1,
    ZOOM_OUT : -1,
    ZOOM_RESTORE_SIZE : 3, //Point to 100
    MOVE_DOWN : 1,
    MOVE_UP :  2,
    MOVE_RIGHT : 3,
    MOVE_LEFT : 4,

    Viewer : function(userParams) {
    
        //// PRIVATE FIELDS //////////////////////////////////////

        this.Params = {
            ZoomFactor : 0.05, // %
            MoveBy : 2, // px
            MoveRate :  15, // ms
            ReversePanning : false,
            ReverseScrolling : true, 
            InnerUpperOffset : -40, // px 
            ShowStatus : true, 
            ShowNavigator : true,
            ShowZoomSelectBox : true,
            IconDirectory : 'icons',
            IconExtension : '.gif', 
            RenderIn: null // an element or an id
        },

        this.isBeingDragged = false;
        this.lastLeft = this.lastTop = this.lastX = this.lastY = null;
        this.originalWidth = this.originalHeight = null;
        this.timeOutId = null;
        this.isMaximizedInstance = false;
        this.onFullWindowListeners = [];
        this.onUnFullWindowListeners = [];

        //// PUBLIC METHODS //////////////////////////////////////////////////////

        this.show = experience.panorama.show;
        this.hide = experience.panorama.hide;
        this.setImage = experience.panorama.setImage;
        this.getImage = experience.panorama.getImage;
		this.doZoom = experience.panorama.doZoom;
        this.addOnFullWindowListener = experience.panorama.addOnFullWindowListener;
        this.removeOnFullWindowListener = experience.panorama.removeOnFullWindowListener;
        this.addOnUnFullWindowListener = experience.panorama.addOnUnFullWindowListener;
        this.removeOnUnFullWindowListener = experience.panorama.removeOnUnFullWindowListener;
		this.restoreImagePosition = experience.panorama.restoreImagePosition;

        //// PRIVATE METHODS /////////////////////////////////////////////////////

        this.getImageResource = experience.panorama.getImageResource;
        this.positionImage = experience.panorama.positionImage;
        this.positionCanvas = experience.panorama.positionCanvas;
        this.setStatus = experience.panorama.setStatus;
        this.fireListeners = experience.panorama.fireListeners;

        //// VALIDATION //////////////////////////////////////////////////////////

        if (
              typeof(userParams['ImageURL']) != 'undefined' &&
              (
                typeof(userParams['ImageWidth']) == 'undefined' || 
                typeof(userParams['ImageHeight']) == 'undefined'
              )
           )
        {
            throw new Error("Experience::Panorama: You have to specify ImageWidth and ImageHeight when calling " +
                              "initialize() passing an ImageURL");
        }

        //// INITIALIZATION /////////////////////////////////////////////
        
        jQuery.extend( this.Params, userParams );
        
        // construct canvas and all
        this.canvas = jQuery(document.createElement('div'));
        this.canvas.addClass("panoramaCanvas");
        this.canvas.css("cursor", experience.panorama.getGrabCursor());
        this.positionCanvas(this.Params['RenderIn'] != null);

        // A queue for childern to be added to the canvas
        var childernQueue = [];
        
        if (this.Params['ShowZoomSelectBox']){
			var zoomPercentages = new Array();
			var start = 100;
			var end = 400;
			var inc = 40;
			while(start<=end){
				zoomPercentages.push(start);
				start += inc;
			}

			this.Params['Controller'].setZoomArray(zoomPercentages);
        }

        if (this.Params['ShowStatus']){
            this.status = document.createElement('span');
            this.status.className = "panoramaStatus";
            childernQueue.push(this.status);
            //this.setStatus(null, "<img src='" + this.getImageResource('loading') + "' />");
        }

        this.image = jQuery(document.createElement('img'));
        this.image.addClass("panoramaImage");
        this.image.attr("src", this.Params['ImageURL']);
        this.image.attr("alt", this.Params['ImageURL']);
        this.image.css("position", "relative");
        this.image.css("width", this.Params['ImageWidth'] + "px");
        this.image.css("height", this.Params['ImageHeight'] + "px");
        this.positionImage();
        childernQueue.push(this.image);
        
        var self = this;
        
        jQuery(this.image).bind('load', function(ev) { 
        		experience.panorama.setStatus(ev, "", self)
        	}
		);
        jQuery(this.image).bind('error', function(ev) { 
        	experience.panorama.setStatus(ev, experience.tr("Could not load image.", experience.panorama.Locales), self);
        });

        // Misc. handlers ..
        jQuery(this.canvas).bind('mousedown', function(ev) {  
                experience.panorama.handleMouseDown(ev, self);
       	});
       	
      	jQuery(this.canvas).bind('mousemove', function(ev) {
                experience.panorama.handleMouseMove(ev, self);
       	});
       	jQuery(document).bind('mouseup', function(ev) {
                experience.panorama.handleMouseUp(ev, self);
       	});
       	jQuery(this.canvas).bind('DOMMouseScroll', function(ev) { 
       		experience.panorama.handleMouseWheel(ev, self);
       	});
       	jQuery(this.canvas).bind('mousewheel', function(ev) { 
       		experience.panorama.handleMouseWheel(ev, self);
       	});

        // add elements to the canvas (in reverse for proper z-Index ordering)
        for(var i = childernQueue.length - 1; i >= 0; i--){
            jQuery(this.canvas).append(jQuery(childernQueue[i]));
        } 
        
    },

    //// PUBLIC METHODS ///////////////////////////////////////////////////////    

    show : function(){
        if(!this.Params['RenderIn']){
            this.fireListeners(this.onFullWindowListeners);
        }

        this.canvas.css('visibility', 'visible');
    },

    hide : function(){
        if(!this.Params['RenderIn']){
            this.fireListeners(this.onUnFullWindowListeners);
        }

        this.canvas.css('visibility', 'hidden');
    },

    toggleFullWindow : function(e){
        //experience.Console.log(this.isMaximizedInstance);

        if (this.isMaximizedInstance){ // minimize
            this.closeImg.src = this.getImageResource('go_fullwindow');
            this.closeImg.title = this.closeImg.alt = experience.tr("Go Full-Window", experience.panorama.Locales);

            this.positionCanvas(true);
            this.isMaximizedInstance = false;
        } else { // maximize
            this.closeImg.src = this.getImageResource('unfullwindow');
            this.closeImg.title = this.closeImg.alt = experience.tr("Restore", experience.panorama.Locales);

            this.positionCanvas(false);
            this.isMaximizedInstance = true;
        }
    },

    getImage : function (){
        return [this.Params['ImageURL'], this.Params['ImageWidth'], this.Params['ImageHeight']];
    },

    setImage : function(url, width, height){
		//this.setStatus(null, "<img src='" + this.getImageResource('loading') + "' />");

        this.Params['ImageURL'] = url;
        this.Params['ImageWidth'] = width + 'px';
        this.Params['ImageHeight'] = height + 'px';

        this.image.attr("src", url);
        this.image.css('width', width + "px");
        this.image.css('height', height + "px");
    },

    addOnFullWindowListener : function(listener){
        this.onFullWindowListeners[this.onFullWindowListeners.length] = listener;
    },

    removeOnFullWindowListener : function(listener){
        if (this.onFullWindowListeners.include(listener)){
            this.onFullWindowListeners.splice(this.onFullWindowListeners.indexOf(listener), 1);
        }
    },

    addOnUnFullWindowListener : function(listener){
        this.onUnFullWindowListeners[this.onUnFullWindowListeners.length] = listener;
    },

    removeOnUnFullWindowListener : function(listener){
        if (this.onUnFullWindowListeners.include(listener)){
            this.onUnFullWindowListeners.splice(this.onUnFullWindowListeners.indexOf(listener), 1);
        }
    },

    //// PRIVATE METHODS ///////////////////////////////////////////////////////

    fireListeners : function(listeners){
        for(var i =0; i < listeners.length; i++){
            listeners[i]();
        }
    }, 

    setStatus : function(e, html, self){
		/*Log.debug("setStatus!"+html);*/
    	if (typeof self != 'undefined' && self != null) {
    		if (self.Params['ShowStatus']){
    			self.status.innerHTML = html;
            }
    	} else if (this.Params['ShowStatus']){
            this.status.innerHTML = html;
        }
    },

    showHelp : function(e){
        alert(experience.tr("HelpText", experience.panorama.Locales));
    },

	restoreImagePosition : function(){
		this.positionImage();
	},

    positionImage :  function (e){
        var image = jQuery(this.image);

        // detecting canvas width and height doesn't work in KHTML (and WebKit?)
        /*if (experience.detectEngine() == "khtml"){
            image.style.top = image.style.left = this.Params.InnerUpperOffset + "px";
            return;
        }*/

        var canvasWidth  = this.Params['CanvasWidth'];
        var canvasHeight = this.Params['CanvasHeight'];
		if(parseFloat(image.css("width")) > canvasWidth){
			image.css("left", ((canvasWidth - parseFloat(image.css("width"))) / 2) + "px");
		}else{
			image.css("left", /*((canvasWidth - parseFloat(image.style.width)) / 2)*/0 + "px");
		}
        image.css("top", ((canvasHeight - parseFloat(image.css("height"))) / 2) + "px");
    },

    positionCanvas : function (isRenderIn){

        if(this.canvas.parentNode){
            this.canvas.parentNode.removeChild(this.canvas);
        }

        if(isRenderIn){
            if(this.isMaximizedInstance){
                this.fireListeners(this.onUnFullWindowListeners);
            }

            jQuery("#" + this.Params['RenderIn']).css("position", "relative");
            jQuery(this.canvas).css("position", "absolute");
            jQuery(this.canvas).css("left", "0px"); 
            jQuery(this.canvas).css("right", "0px");
            jQuery(this.canvas).css("top", "0px");
            jQuery(this.canvas).css("bottom", "0px");
            jQuery(this.canvas).css("height", "100%");
            jQuery(this.canvas).css("width", "100%");
            jQuery(this.canvas).css("visibility", 'visible');

            jQuery("#" + this.Params['RenderIn']).append(jQuery(this.canvas));
        } else {
            if(this.Params['RenderIn']){
                this.fireListeners(this.onFullWindowListeners);
            }

            document.getElementsByTagName('body')[0].appendChild(this.canvas[0]);

            if (experience.detectBrowser() == "ie5" || experience.detectBrowser() == "ie6"){
                this.canvas.css("position", "absolute");
                this.canvas.css("top", 
                    (ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px");
                this.canvas.css("height",
                     experience.panorama.getInnerWindowDimensions()['height']);
                this.canvas.css("width",
                     experience.panorama.getInnerWindowDimensions()['width']);
            } else {
                this.canvas.css("position", "fixed");
            }
        }

        //experience.Console.log(this.canvas.parentNode.tagName + "," + isRenderIn);
    },

    /**
      * Thanks to http://www.quirksmode.org/viewport/compatibility.html 
      */
    getInnerWindowDimensions: function (){
        var x,y;
        if (self.innerHeight) // all except Explorer
        {
            x = self.innerWidth;
            y = self.innerHeight;
        }
        else if (document.documentElement && document.documentElement.clientHeight)
            // Explorer 6 Strict Mode
        {
            x = document.documentElement.clientWidth;
            y = document.documentElement.clientHeight;
        }
        else if (document.body) // other Explorers
        {
            x = document.body.clientWidth;
            y = document.body.clientHeight;
        }

        return {width: x, height: y};
    },

	restoreZoom : function(e, index){
		var c = this.Params['Controller'];
		c.doZoom(index);
	},

    zoom : function(e, delta, self){
		var c = (typeof self != 'undefined' && self != null) ? self.Params['Controller'] : this.Params['Controller'];
		var index = c.getZoomIndex(delta);

		c.doZoom(index);
    },

    clearMoveTimeout : function (e){
         clearTimeout(this.timeOutId);
    },

	doZoom:function(el, index, percentage){
		this.Params['Controller'].setZoom(percentage);
		percentage = percentage/100;
        if (percentage != '#'){
            var newWidth, newHeight, newLeft, newTop;

            var c = this.Params['Controller'];

            newWidth  = c.getOriginalWidth() * percentage;
            newHeight = c.getOriginalHeight() * percentage;
            
            // distribute size change
			if(newWidth>this.Params['CanvasWidth']){
				newLeft = parseFloat(this.image.css("left")) - 
								((newWidth - parseFloat(this.image.css("width"))) / 2);
			}else{
				newLeft = (this.Params['CanvasWidth'] - newWidth) / 2;
			}
			if(newHeight>this.Params['CanvasHeight']){
				newTop  = parseFloat(this.image.css("top")) - 
								((newHeight - parseFloat(this.image.css("height"))) / 2); 
			}else{
				newTop  = (this.Params['CanvasHeight'] - newHeight) / 2;
			}
			
			if (percentage == 1) {
				newLeft = 0;
			}
            
            // apply new size
            this.image.css("width", newWidth  + "px");
            this.image.css("height", newHeight + "px");

            this.image.css("left", newLeft   + "px");
            this.image.css("top", newTop    + "px");
            
            if (el) {
            	el.selectedIndex = index;
            }

			var me = this;
			c.wait(function(){
				c.setSize(parseInt(newWidth),parseInt(newHeight));
				me.setImage(c.getURL(), c.getWidth(), c.getHeight());
			});
        }
	},
	
    handleZoomPercentageChange : function(e){
        var percentage = Event.element(e).value;
		this.Params['Controller'].doZoom(this.Params['Controller'].getIndex(percentage));
    },

    handleMouseDown : function(e, self){
    	var elem = jQuery(e.target);
        if (elem != null && (elem[0] == self.canvas[0] || elem[0] == self.image[0]) && 
        		(!jQuery.browser.msie && e.button == 0) || (jQuery.browser.msie && e.button == 1)) {

        	self.isBeingDragged = true;
            self.canvas.css("cursor", experience.panorama.getGrabbingCursor());
            self.lastLeft = parseFloat(self.image.css("left"));
            self.lastTop  = parseFloat(self.image.css("top"));
            self.lastX = e.pageX;
            self.lastY = e.pageY;
            
            // I love jQuery!
            e.preventDefault();
        }
    },

    handleMouseMove : function (e, self){
    	var elem = jQuery(e.target);
        if (elem != null && (elem[0] == self.canvas[0] || elem[0] == self.image[0])) {
        	
                var sign = self.Params['ReversePanning'] ? -1 : 1;
                if (self.isBeingDragged){
                	self.image.css("left", self.lastLeft + (sign * (e.pageX - self.lastX)) + "px"); 
                	self.image.css("top", self.lastTop  + (sign * (e.pageY - self.lastY)) + "px"); 
                }
                
                e.preventDefault();
        }
    },

    handleMouseUp : function(e, self){
    	self.isBeingDragged = false;
    	self.canvas.css("cursor", experience.panorama.getGrabCursor());
    },

    getGrabCursor : function(e) {
        if(experience.detectEngine() == "gecko"){
            return '-moz-grab';
        } else {
            return 'move';
        }
    },

    getGrabbingCursor : function(e){
        if(experience.detectEngine() == "gecko"){
            return '-moz-grabbing';
        } else {
            return 'move';
        }
    },

    /**
     * See also 
     *  http://adomas.org/javascript-mouse-wheel/
     *  http://www.ogonek.net/mousewheel/demo.html
     */
    handleMouseWheel: function(event, self){
        var delta = 0;

        if (event.wheelDelta) { // IE/Opera. 
            delta = event.wheelDelta/120;
            //In Opera 9, delta differs in sign as compared to IE.
            if (window.opera)
                delta = -delta;
        } else if (event.detail) { // Mozilla case. 
                // In Mozilla, sign of delta is different than in IE.
                // Also, delta is multiple of 3.
                delta = -event.detail/3;
        }

        delta = Math.round(delta); //Safari Round

        // If delta is nonzero, handle it.
        // Basically, delta is now positive if wheel was scrolled up,
        // and negative, if wheel was scrolled down.
        if (delta){
                experience.panorama.zoom('', delta, self);
        }

        Event.stop(event);
    },

    getImageResource : function (resource){
            return this.Params['IconDirectory'] + "/" + resource 
                                        + this.Params['IconExtension'];
    },


    Locales : {
        'en_US' : {
            'HelpText' : "Grab (click and drag) the image to move it around. Use your mouse wheel to zoom in and out the image or, if you don't have one, use the zoom icons in the toolbar. If you hover with your mouse pointer over any of the navigator arrow in the bottom right corner, the image will starting moving in that direction (automatic scrolling.)\n\nPanorama is part of the Experience JavaScript Library.\nFor details, see project website at http://experience.sf.net\n"
        },

        'ar' : {
            'Zoom In' : '?????',
            'Zoom Out' : '?????',
            'Restore Original Size' : '??????? ????? ??????',
            'Restore Original Position' : '??????? ?????? ??????',
            'Close' : '?????',
            'Scrolling .. ' : '???? ??????? ...',
            'Help': '??????',
            'HelpText' :  '???? (???? ? ??) ?????? ?? ??????. ?????? ???? ?????? ?????? ?? ????? ??????. ??? ?? ??? ???? ?????? ??????? ????????? ???? ??? ???? ???????. ??? ???? ???? ?????? ??? ???? ???? ?????? ?? ?????? ?????? ??????? ???? ????? ?????? ?? ?? ??? ??????? (????? ??????). \n\n ???????? ?? ??? ?? ????? ???????? ???????? ??????????. \n ????? ?? ????????? ???? ????? ???? ??????? ?? \n http://experience.sf.net',
            'Could not load image' : '??? ????? ????? ??????'
            ,'Go Full-Window' : '???? ??????? ???????',
            'Restore' : '???????'
        }
    }
}


