﻿/*
 * @private (class) : Slider
 * Define properties
 */ 
window.sliderElements = {};
var Slider = function(params)
{
	var _this = this;
	
	var _getAction = function(elm)
	{
		if (typeof elm == "object"/* && typeof elm.getAttribute != "undefined"*/)
		{
			var 
				_action = elm["data-action"] || elm.getAttribute("data-action") || null,
				_context = elm["data-slider"] || elm.getAttribute("data-slider") || null,
				_parentId = _this.elements["container"].id
			;
			if (_context === _parentId)
			{
				return _action;
			}
		}
		return false;
	};

	driver.extend(this, params);
	this.events = [];
	
	//
	// Methodes privées étendues 
	// lors de l'instanciation
	//
	_setAbstract.call(this, 
	{
		"gotoSlide": function(e)
		{
			// Donot care of <span> including in <a>
			var 
				elm = e.target,
				_parent = elm.parentNode || null,
				_this = this
			;
			if (elm && typeof elm.tagName == "string" && _parent && _parent.tagName.toLowerCase() == "a")
			{
				elm = elm.parentNode || {};
			}
			
			var
				type = e.type || null,
				isInit = (type == "start") || !this.initialized,
				action = e.action || _getAction(elm),
				indice = this.getIndice(elm),
				isInfinite = this.animation.infinite || false
			;
			
			//_trace("gotoSlide : " + indice + " (" + (typeof indice) + ", " + type + ", busy=" + this.busy + ")", elm);

			if (typeof indice != "number" && !action && !isInit)
			{
				return false;
			}
			
			if (isInit)
			{
				this.startIndice = indice;	
			}
			
			if (type == "click")
			{
				e.preventDefault();
			}
			
			var currentIndice = this.indice;
			indice = (isInit) ? this.startIndice : ((typeof indice == "undefined") ? currentIndice : indice);			
			//_trace(currentIndice + ", " + indice)

			// Calcul de l'indice pour les boutons Suivant/Précédent
			// Récupération de la slide concernée
			if (typeof action == "string")
			{
				var
					minIndice = 0,
					maxIndice = this.getSlides().length
				;
				
				// Boucle aux bornes l'animation ou pas
				indice = (action == "back") ? currentIndice-1 : currentIndice+1;
				if(isInfinite)
				{
					indice = (indice < minIndice) ? maxIndice-1 : indice%maxIndice;
				}
				else
				{
					indice = (indice >= maxIndice) ? maxIndice-1 : ((indice <= minIndice) ? minIndice : indice);
				}
				
				if (indice < minIndice || indice >= maxIndice)
				{
					return false;
				}
			}
			
			// Empilement dans la file d'attente
			if (type != "queued" && type != "history")
			{
				if (this.history.length <= this.maxQueue)
				{
					var 
						_lastHistory = this.history.last(),
						_lastIndice = _lastHistory ? _lastHistory.indice : null
					;	
					if (indice != _lastIndice && (indice != currentIndice && currentIndice!= null))
					{
						this.history.push({"indice": indice, "action": action || null});
					}
					indice = (typeof _lastIndice == "number") ? _lastIndice : indice;
				}
			}
			
			// Stop if Slider is playing
			if (this.busy || typeof indice != "number" || (currentIndice == indice && !isInit))
			{
				return false;
			}

			// Select new Slide
			this.busy = true;			
			this.oldIndice = (isInit || indice==currentIndice) ? null : currentIndice;
			this.indice = indice;	
			this.animation.action = action || ((this.oldIndice-this.indice)>0 ? "back" : "next");

			// Identify old & new slide
			var 
				_slides = this.getSlides(),
				_oldSlide = _slides[this.oldIndice],
				_newSlide = _slides[this.indice]
			;
			// Bouger les éléments dans le DOM 
			// pour les placer dans l'ordre de "lecture"
			if(isInfinite && _oldSlide)
			{
				var 
					_previousNode = driver.previousElement(_oldSlide),
					_nextNode = driver.nextElement(_oldSlide),
					_action = this.animation.action
				;
				if(_action == "back" && _previousNode != _newSlide)
				{
					_oldSlide.insert({"before": _newSlide});
				}
				else if (_action == "next" && _nextNode != _newSlide)
				{
					_oldSlide.insert({"after": _newSlide});
				}
			}
			
			setTimeout(function()
			{
				driver.addClass(_oldSlide, _this.className["oldSlide"]);
				driver.addClass(_newSlide, _this.className["newSlide"]);
				
				// Launch "onchange" callback
				if (typeof _this["onchange"] != "undefined" && !isInit)
				{
					_this["onchange"].call(_this);
				}
			}, 20);

			// Debug
			//_trace("gotoSLide : (start) " + this.startIndice + ", (type) " + type + ", (Old Slide) indice=" + this.oldIndice + ", (New Slide) indice=" + this.indice);
			
			return e;
		},
		
		/*
		 * @private (method) : animate
		 * default animation
		 */
		"animate": function(params)
		{
			/*
			if (typeof this.resizeSlides != "undefined")
			{
				this.resizeSlides.resize();
			}			
			*/
			driver.extend(this.animation, params);
			driver.addClass(this.elements["container"], "slide-animation");
			return this.animation;
		},
		
		"onchange" : function()	{return true;},
		
		"onsuccess" : function(elm)	
		{
			driver.removeClass(this.elements["container"], "slide-animation");
			if (typeof this.resizeSlides != "undefined")
			{
				//this.resizeSlides.reset();
				this.resizeSlides.resize();
			}
			return elm;
		},
		
		"save": function()
		{
			//_trace("Slider : (private) save : ", this)

			var _sliderID = this.elements["container"].id || false;
			if (!_sliderID)
			{
				_sliderID = "slider-" - Object.keys(sliderElements).length;
				this.elements["container"].id = _sliderID;
			}			
			if (typeof sliderElements == "undefined")
			{
				window.sliderElements = {};
			}
			sliderElements[_sliderID] = this;
			return true;
		},
		
		"stop": function()
		{
			_trace("Slider : (private) stop : ", this.currentAnimation);

			delete this.currentAnimation;		
			this.history = [];
			this.currentAnimation = null;
			return true;
		},
		
		"destroy": function()
		{
			_trace("Slider : (private) destroy : ", this);
			
			this.stop();

			// Suppression des événements
			var events = this.events || [];
			var i=0;
			while(events.length)
			{
				var args = events[0] || ['', '', ''];
				driver.stopEvent(args[0], args[1], args[2]);
				events.pop();
			}
			
			// Init du DOM
			this.elements = {
					"container" : [],
					"content" : [], // Utile pour le resize uniquement
					"links" : [],
					"slides" : [],
					"resizable": null
				};

			// Init des paramètres
			this.events = [];
			this.indice = null;
			this.startIndice = null;
			this.oldIndice = null;	
			this.busy = false;
			this.initialized = null;			
			this.currentLinks = [];
			this.currentAnimation = null;
			
			return true;
		},
		
		/*
		 * @private (method) : init
		 * default inititialse
		 */ 
		"init": function(elements)
		{
			//_trace("slider : (private) init ", arguments)

			this.elements = elements;
			
			// FIXME : PATCH temporaire car
			// Bug de référence : si plusieurs instances dans la page alors
			// cette propriété devient commune à toutes les instances
			// uniquement si c'est proprétés sont initialisé via Accordion.proptotype.history
			this.history = [];
			
			if (typeof this.elements["container"] == "undefined" || !this.elements["container"])
			{
				return false;
			}
			
			if (this.animation && typeof this.elements["content"] != "undefined")
			{
				driver.addClass(this.elements["container"], "scroll-animation");
				this.animation["scrollLeft"] = this.elements["content"].scrollLeft;
				this.animation["scrollTop"] = this.elements["content"].scrollTop;
				
				if (typeof this.animation["direction"] == "undefined"  || !this.animation["direction"])
				{
					this.animation["direction"] = "horizontal";
				}
			}
			
			// Liens "Aller à la slide 'n'"
			_setLinks.call(this);
			
			var _resizableReferer = this.elements["resizable"] || null;
			if (_resizableReferer)
			{
				this.resizeSlides = new fullscreenElm();
				this.resizeSlides.init(_resizableReferer, {"elements": this.getSlides()});				
				this.resizeSlides.resize();
			}
			
			// Enregistrement			
			this.save();
			
			return true;
		}
	});
	
	/*
	 * @private (method) : setLinks
	 * get links which show/hide frames
	 */	
	var _setLinks = function()
	{
		var 
			_this = this,
			_handleClick = function(e)
			{
				//e.stopPropagation();
				_this.gotoSlide.call(_this, e);
			}
		;
		this.events.push([this.elements["container"], this.typeEvent, _handleClick]);
		driver.addEvent(this.elements["container"], this.typeEvent, _handleClick);		
		return true;
	};
};
Slider.prototype = 
{
	"elements":
	{
		"container" : [],
		"content" : [], // Utile pour le resize uniquement
		"links" : [],
		"slides" : [],
		"resizable": null
	},
	"events": [],
	
	"busy": false,
	"initialized": null,
	
	"currentLinks": [],
	"currentAnimation": null,
	
	// FIXME : ranger dans un objet commun
	/*
	indice:
	{
		min: 0,
		max: null,
		current: null,
		start: 0,
		old: null
	}
	*/
	"indice": null,	
	"startIndice": null,
	"oldIndice": null,
	
	// FIXME : ranger dans un objet commun
	/*
	history:
	{
		queue: [],
		maxQueue: 2
	}
	*/
	"history": [],
	"maxQueue": 10,	
	
	"typeEvent": "click",	
	"animation":
	{
		"delay": 0,
		"scrollLeft": 0,
		"scrollTop": 0,
		"infinite": false,
		"direction": "horizontal",
		"playOnce": false
	},
	
	"className":
	{
		"active": "active",
		"newSlide": "newSlide",
		"oldSlide": "oldSlide"
	},	
	
	"getLinks": function(params)
	{
		var _links = this.elements["links"] || [];
		
		if (typeof params == "object")
		{
			var
				_result = [],
				_sliderRef = this.elements["container"].id
			;
			for(var i=0, len=_links.length; i<len; i++)
			{
				var _link = _links[i];				
				if (_link.getAttribute("data-slider") != _sliderRef)
				{
					continue;
				}
				
				for (var key in params)
				{
					if (_link.getAttribute(key) == params[key])
					{
						_result.push(_link);
					}
				}
			}
			return _result;
		}
		
		return _links;
	},
	
	"getSlides": function(className)
	{
		return this.elements["slides"] || [];
	},
	
	"getIndice": function(elm)
    {
		if (typeof elm == "undefined" || typeof elm.getAttribute == "undefined")
		{
			return false;
		}
		var 
			_indice = elm.getAttribute("data-slide") || null,
			_context = elm.getAttribute("data-slider") || null,
			_parentId = this.elements["container"].id
		;
		if (_context === _parentId)
		{
			return _indice*1;
		}
        return false;
    },
    
    "getStartSlide" : function()
    {
    	var 
    		_slides = this.getSlides(),
    		_classActive = this.className["active"]
    	;
		for (var i=0, len=_slides.length; i<len; i++)
		{
			var 
				_slide = _slides[i],
				_isActive = driver.hasClass(_slide, _classActive)
			;
			if (_isActive)
			{
				return _slide;
			}
		}
		return _slides[this.startIndice] || _slides[0] || false;
    },
	
	"gotoSlide": function(e)
	{
		if (!e)
		{
			return false;
		}
		
		var
			_this = this,
			indice = this.indice,
			oldIndice = this.oldIndice,
			
			slides = this.getSlides() || [],
			oldSlide = slides[oldIndice],
			newSlide = slides[indice]
		;

		//_trace("(public) gotoSlide : " + this.elements["container"].id + " : " + this.indice + ", type=" + e.type)
		if (!newSlide || (oldSlide == newSlide && e.type != "start"))
		{
			return false;
		}
		
		setTimeout(function()
		{
			_this.animate({
				"newSlide": newSlide,
				"oldSlide": oldSlide
			});
			
		}, 20);
	},

	
	/*
	 * @public (method) : _activeSlide
	 * End of the animation : select current slide
	 */ 
	"activeSlide": function(newSlide, oldSlide)
	{
		if (!newSlide)
		{
			return false;
		}
		
		var 
			classActive = this.className["active"],
			classOld = this.className["oldSlide"],
			classNew = this.className["newSlide"]
		;
		
		// Reset the old slide
		if (oldSlide)
		{
			driver.removeClass(oldSlide, classActive);
			driver.removeClass(oldSlide, classOld);
		}
		
		// Set the new slide as currentSlide
		driver.addClass(newSlide, classActive);
		driver.removeClass(newSlide, classNew);

		//
		// Activation des liens
		//
		var i, len;
		for (i=0, len=this.currentLinks.length; i<len; i++)
		{
			driver.removeClass(this.currentLinks[i], classActive);
		}
		this.currentLinks = this.getLinks({"data-slide": this.indice});
		for (i=0, len=this.currentLinks.length; i<len; i++)
		{
			driver.addClass(this.currentLinks[i], classActive);
		}
		
		// Remove from history
		this.busy = false;
		this.history.shift();
		
		// Launch "success" callback
		if (typeof this["onsuccess"] != "undefined")
		{
			this["onsuccess"].call(this, newSlide);
		}
		
		// Goto the next item queued
		if (this.history.length)
		{
			var
				_currentHistory = this.history.first(),
				_currentIndice = _currentHistory.indice,
				_action = _currentHistory.action,
				_slides = this.getSlides()
			;
			this.gotoSlide(
			{
				"target": _slides[_currentIndice], 
				"indice": _currentIndice,
				"type": "queued",
				"action": _action
			});
		}
	},

	/*
	 * Callbacks 
	 */
	
	"onStartChange" : function(){},	
	"onchange" : function()
	{
		//_trace("(default) onchange")
	},
	"onsuccess" : function(elm){},
	
	"animate": function(params)
	{
		if (typeof params != "object" || !params)
		{
			return false;
		}
		var
			_this = this,
			_oldSlide = params.oldSlide || null,
			_newSlide = params.newSlide || null,
			_contentElm = this.elements["content"],			
			_delay = this.animation.delay,
			_direction = this.animation.direction,
			_activeSlide = function()
			{
				_this.activeSlide(_newSlide, _oldSlide);				
				_this.stop();
			}
		;

		if (_direction == "horizontal")
		{
			// FIXME : cette animation affiche ttes les slides
			// même durant l'animation
			// FIXME : buguée sur IE7
			/*
			var _maxLeft = (_oldSlide ? _oldSlide.offsetLeft : 0) - _newSlide.offsetLeft;
			_this.elements["content"].scrollLeft -= _maxLeft;
			_this.animation["scrollLeft"] -= _maxLeft;			
			driver.animateCSS(_contentElm, {"marginLeft":  _this.animation["scrollLeft"]*-1 + "px"}, _delay, _activeSlide);
			*/
			
			// Cette animation permet d'aller d'une slide à une autre,
			// sans passer par les slides intermédiaires
			var _action = this.animation.action;
			_direction = (_action == "back") ? 1 : -1;
			
			var
				_minLeft = 0,
				_maxLeft =  _direction * _newSlide.offsetWidth
			;

			//_trace("animate : " + _minLeft + ", " + _action, _oldSlide ? _oldSlide.offsetLeft : null);
			if (_action == "back")
			{
				if (_oldSlide)
				{
					driver.changeCSS(_oldSlide, {"marginLeft" : _minLeft + "px"});
				}
				driver.changeCSS(_newSlide, {"marginLeft" : (-1*_maxLeft) + "px"});
				this.currentAnimation = driver.animateCSS(_newSlide, {"marginLeft": _minLeft + "px"}, _delay, _activeSlide);
				return false;
			}
			
			else if (_action == "next")
			{
				driver.changeCSS(_newSlide, {"marginLeft" : _minLeft + "px"});
				if (_oldSlide)
				{
					driver.changeCSS(_oldSlide, {"marginLeft" : _minLeft + "px"});
					this.currentAnimation = driver.animateCSS(_oldSlide, {"marginLeft" : _maxLeft + "px"}, _delay, _activeSlide);
					return false;
				}
			}
			_activeSlide();
			return false;
		}
		/*
		else if (_direction == "vertical")
		{
			//_trace("SLIDER : animate : descendre ou monter ?")
		}
		*/
		
		_activeSlide();
		
		return false;
	},
	
	"save": function() {},
	"stop": function() {},
	"destroy": function(){},
	
	"init": function(params)
	{
		this.gotoSlide({
			target: this.getStartSlide(),
			type: "start"
		});
		this.initialized = true;
	}
};
