

/**
 * Fichier obsolète : utiliser ev/gui/slider.js à la place.
 */
(function() {
	var window = this,
			ev = window.ev;

	if (window.console && window.console.error) {
		window.console.error('FATAL : effects/slider.js est DEPRECATED !! => Utiliser ev/gui/slider.js à la place.');
	}

	if (!ev.effects) {
		ev.effects = {};
	}

	if (!ev.effects.Slider) {
		ev.effects.Slider = {};
	}

	ev.effects.Slider = (function() {
		// Méthode anonyme définissant un espace privé pour éviter de
		// créer des variables globales inutiles en dehors de ce fichier.

		/**
		 * This private constant defines the delay of the timeline internally used
		 * by the slider. CAUTION: modifying this constant can cause a strong
		 * resource consumption.
		 */
		var INTERVAL = 60;

		/**
		 * private constant which defines the different sliding actions.
		 */
		var Direction = {
			/** no sliding action. */
			NONE: 0,
			/** opening sliding action. */
			OPEN: 1,
			/** closing sliding action. */
			CLOSE: 2
		};

		/**
		 * This is the Slider's constructor.
		 *
		 * This class manages a slider, i.e. a block that can change fluently its
		 * dimensions vertically and/or horizontally. The size of the bloc evolve from
		 * min-height to max-height and from min-width to max-width applied to the
		 * block. CAUTION: Those values MUST be specified with px unity, ortherwise they
		 * are not read by slider.
		 * If min-height or max-height (resp. min-width or max-width) is missing, the
		 * block is not moving vertically (resp. horizontally).
		 * To open/close the block we must use {@link #open()}, {@link #close()}, or
		 * {@link #slide()}.
		 * A SliderListener can be added to the Slider, in order to add behaviours drove
		 * by SliderEvent @see {@link EV.effects.SliderEvent} for further informations.
		 *
		 * Usage:
		 *  <!-- The following block has min-width and max-width and then will slide
		 *  horizontally, not vertically. Those styles can be placed in a style
		 *  tag, or in a CSS file.
		 *   -->
		 *  <div id="x" style="min-width: 100px; max-width: 400px;">...</div>
		 *  <script type="text/javascript">
		 *  // Defines the block's DOM element.
		 *  var block=document.getElementById("x");
		 *  // Defines the slider affected to the block, it will open/close in 3s.
		 *  ar slider=new EV.effects.Slider(block,3000);
		 *  // Defines additional behavior of slider in an overwriting
		 *  // SliderListener class.
		 *  function MySliderListener() {
		 *      this.throwSliderEvent=function(sliderEvent) {
		 *        // Implement here behaviour of listener
		 *      }
		 *    }
		 *    slider.addSliderListener(new MySliderListener());
		 *  </script>
		 *  <!-- A click on the following block toggles between open and close -->
		 *  <span onclick="slider.slide()">slide</span>
		 *
		 * @param _paneNode A DOM element designing the block to slide.
		 * @param _delay The delay interval (in ms) for the block to open or close.
		 * @param _opened A boolean which defines if the block is initially opened (if
		 * true). This parameter is optional, default is false (block closed).
		 * @throws If _paneNode is undefined.
		 * @throws If _paneNode is null.
		 * @throws If _delay is undefined.
		 * @throws If _delay is null.
		 * @throws If _delay is not a number.
		 * @throws If _delay is negative.
		 */
		return function(_paneNode, _delay, _opened) {
			if (!_paneNode) {throw new Error('paneNode is not valid');}
			if (typeof(_delay) !== 'number') {throw new Error('delay is not a number');}
			if (_delay < 0) {throw new Error('delay is negative');}
			if (_delay < 200) {throw new Error('delay is too small (minimal value is 200ms)');}

			/**
			 * This property defines if the sliding block is initially opened.
			 * Its default value is 'false'.
			 * '!!_opened' means 'NOT NOT OPENED'. This should transform the parameter,
			 * whatever its type is, to a boolean value :
			 *   - null, undefined, 0, '', false => false
			 *   - object, number != 0, true => true
			 */
			_opened = !!_opened;

			/**
			 * Internal reference (using closure) to be accessed by the sliderTimelineListener.
			 */
			var thisSlider = this;

			/**
			 * This private property holds the actual or precedent slide type (defined
			 * by on of the three precedent constants).
			 */
			var direction = Direction.NONE;

			/**
			 * The timeline used by this slider to manage the effect
			 * FIXME [ygally] rendre unique et globale (à tout slider d'une même page)
			 * @see {@link Timeline}.
			 */
			var timeline = new window.Timeline(INTERVAL);

			/**
			 * This property defines the {@link SliderListener} associated with this
			 * Slider, default is null which indicates there is no SliderListener
			 * defined for this instance.
			 */
			var sliderListener = null;

			/**
			 * Gets the pane node, i.e; the sliding block.
			 */
			this.getPaneNode = function() {
				return _paneNode;
			};

			/**
			 * This property holds the minimum height of the sliding block (of the
			 * closed block). It is defined by min-height style if defined, null
			 * otherwise. If null, the block does not open vertically. This size must be
			 * defined in px unit.
			 */
			var minHeight = window.getStyleValue(_paneNode, 'min-height');// equals "none" if not defined
			if (minHeight !== 'none' && minHeight !== '-1px' && minHeight.match(/[0-9]+px/)) {
				minHeight = window.parseInt(minHeight.replace(/px/, ''), 10);
			}
			else {
				minHeight = null;
			}

			/**
			 * This property holds the maximum height of the sliding block (of the
			 * opened block). It is defined by max-height style if defined, null
			 * otherwise. If null, the block does not open vertically. This size must be
			 * defined in px unit.
			 */
			var maxHeight = window.getStyleValue(_paneNode, 'max-height');
			if (maxHeight !== 'none' && maxHeight !== '-1px' && maxHeight.match(/[0-9]+px/)) {
				maxHeight = parseInt(maxHeight.replace(/px/, ''), 10);
			}
			else {
				maxHeight = null;
			}

			/**
			 * This property holds the minimum width of the sliding block (of the
			 * closed block). It is defined by min-width style if defined, null
			 * otherwise. If null, the block does not open horizontally. This size must
			 * be defined in px unit.
			 */
			var minWidth = window.getStyleValue(_paneNode, 'min-width');
			if (minWidth !== 'none' && minWidth !== '-1px' && minWidth.match(/[0-9]+px/)) {
				minWidth = parseInt(minWidth.replace(/px/, ''), 10);
			}
			else {
				minWidth = null;
			}

			/**
			 * This property holds the maximum width of the sliding block (of the
			 * closed block). It is defined by max-width style if defined, null
			 * otherwise. If null, the block does not open horizontally. This size must
			 * be defined in px unit.
			 */
			var maxWidth = window.getStyleValue(_paneNode, 'max-width');
			if (window.genericNavigator.navigator.id === window.SAFARI) {
				// Bug sur SAFARI : max-width retourne la valeur de max-height => il faut procéder autrement
				if (minHeight === null || maxHeight === null) {
					// si 1 des paramètres min-height et max-height n'est pas défini,
					// on utilise la largeur de l'élément (width) comme max-width et on
					// on construit donc un Slider qui fait varier la largeur
					maxWidth = window.getStyleValue(_paneNode, 'width');
				}
				else {
					maxWidth = 'none';
				}
			}

			if (maxWidth !== 'none' && maxWidth !== '-1px' && maxWidth.match(/[0-9]+px/)) {
				maxWidth = window.parseInt(maxWidth.replace(/px/, ''), 10);
			}
			else {
				maxWidth = null;
			}

			/**
			 * This private property holds the effective height of the sliding block as
			 * it evolves during opening and closing operations. If the block does not
			 * slide vertically, this property is undefined.
			 */
			var height;

			/**
			 * This property defines the vertical speed of sliding box, defined in term
			 * of increment applied to height on every slider's timeline delay.
			 */
			var incrementHeight;
			function initHeightParameters() {
				if (minHeight !== null && maxHeight !== null) {
					// If minHeight AND maxHeight are defined the block slides vertically
					// and height and incrementHeight can be defined. Initial value of
					// height is defined by opened property.
					if (_opened) {
						height = maxHeight;
					}
					else {
						height = minHeight;
					}
					_paneNode.style.height = height + 'px';
					incrementHeight = (maxHeight - minHeight) / _delay * INTERVAL;
				}
				else {
					_paneNode.style.height = window.getStyleValue(_paneNode, 'height');
				}
			}
			initHeightParameters();

			/**
			 * This private property holds the effective width of the sliding block as
			 * it evolves during opening and closing operations. If the block does not
			 * slide horizontally, this property is undefined.
			 */
			var width;

			/**
			 * This property defines the horizontal speed of sliding box, defined in
			 * term of increment applied to width on every slider's timeline delay.
			 */
			var incrementWidth;
			function initWidthParameters() {
				if (minWidth !== null && maxWidth !== null) {
					// See over, same considerations.
					if (_opened) {
						width = maxWidth;
					}
					else {
						width = minWidth;
					}
					_paneNode.style.width = width + 'px';
					incrementWidth = (maxWidth - minWidth) / _delay * INTERVAL;
				}
				else {
					_paneNode.style.width = window.getStyleValue(_paneNode, 'width');
				}
			}
			initWidthParameters();

			/**
			 * Sets minHeight (force the value defined by style min-height).
			 * @param _minHeight Number of pixels.
			 */
			this.setMinHeight = function(_minHeight) {
				if (typeof(_minHeight) !== 'number') {throw new Error('minHeight is not a number');}
				minHeight = _minHeight;
				initHeightParameters();
			};

			/**
			 * Sets maxHeight (force the value defined by style max-height).
			 * @param _maxHeight Number of pixels.
			 */
			this.setMaxHeight = function(_maxHeight) {
				if (typeof(_maxHeight) !== 'number') {throw new Error('maxHeight is not a number');}
				maxHeight = _maxHeight;
				initHeightParameters();
			};

			/**
			 * Sets minWidth (force the value defined by style min-width).
			 * @param _minWidth Number of pixels.
			 */
			this.setMinWidth = function(_minWidth) {
				if (typeof(_minWidth) !== 'number') {throw new Error('minWidth is not a number');}
				minWidth = _minWidth;
				initWidthParameters();
			};

			/**
			 * Sets maxWidth (force the value defined by style max-width).
			 * @param _maxWidth Number of pixels.
			 */
			this.setMaxWidth = function(_maxWidth) {
				if (typeof(_maxWidth) !== 'number') {throw new Error('maxWidth is not a number');}
				maxWidth = _maxWidth;
				initWidthParameters();
			};

			/**
			 * Internal instance of {@link TimelineListener}, containing the
			 * particular behavior of this slider.
			 *
			 * When a slider is opening or closing, its timeline starts (it stops when
			 * this operation ends), then during sliding
			 * {@link TimelineListener.throwTimelineEvent} is called, this method then
			 * manages all aspects of sliding.
			 */
			var sliderTimelineListener = new window.TimelineListener();

			/**
			 * Overrides the default {@link TimelineListener.throwTimelineEvent} method.
			 * This method defines the particular behavior of this slider, (i.e. managing
			 * the sliding period).
			 *
			 * @param {TimelineEvent} _e : the TimelineEvent object, generated by the timeline.
			 */
			sliderTimelineListener.throwTimelineEvent = function(_e) {
				// Sliding only append when the timeline is running (starting is
				// useless).
				if (_e.getType() === window.TimelineEventType().RUNNING) {
					if (direction === Direction.OPEN) {
						if (minHeight !== null && maxHeight !== null) {
							if (height < maxHeight) {
								height = height + incrementHeight;
								if (height > maxHeight) {
									_paneNode.style.height = maxHeight + 'px';
								}
								else {
									_paneNode.style.height = Math.ceil(height) + 'px';
								}
							}
						}
						if (minWidth !== null && maxWidth !== null) {
							if (width < maxWidth) {
								width = width + incrementWidth;
								if (width > maxWidth) {
									_paneNode.style.width = maxWidth + 'px';
								}
								else {
									_paneNode.style.width = Math.ceil(width) + 'px';
								}
							}
						}
						if ((minHeight === null || maxHeight === null || height >= maxHeight) && (minWidth === null || maxWidth === null || width >= maxWidth)) {
							timeline.stop();
							if (minHeight !== null && maxHeight !== null) {
								height = maxHeight;
								_paneNode.style.height = Math.ceil(height) + 'px';
							}
							if (minWidth !== null && maxWidth !== null) {
								width = maxWidth;
								_paneNode.style.width = Math.ceil(width) + 'px';
							}
							if (sliderListener !== null) {
								sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.OPEN_STOP, height, width, thisSlider));
							}
						}
						else {
							if (sliderListener !== null) {
								sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.OPEN_RUNNING, height, width, thisSlider));
							}
						}
					}
					else if (direction === Direction.CLOSE) {
						if (minHeight !== null && maxHeight !== null) {
							if (height > minHeight) {
								height = height - incrementHeight;
								if (height < 0) {
									_paneNode.style.height = '0px';
								}
								else {
									_paneNode.style.height = Math.ceil(height) + 'px';
								}
							}
						}
						if (minWidth !== null && maxWidth !== null) {
							if (width > minWidth) {
								width = width - incrementWidth;
								if (width < 0) {
									_paneNode.style.width = '0px';
								}
								else {
									_paneNode.style.width = Math.ceil(width) + 'px';
								}
							}
						}
						if ((minHeight === null || maxHeight === null || height <= minHeight) && (minWidth === null || maxWidth === null || width <= minWidth)) {
							timeline.stop();
							if (minHeight !== null && maxHeight !== null) {
								height = minHeight;
								_paneNode.style.height = Math.ceil(height) + 'px';
							}
							if (minWidth !== null && maxWidth !== null) {
								width = minWidth;
								_paneNode.style.width = Math.ceil(width) + 'px';
							}
							if (sliderListener !== null) {
								sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.CLOSE_STOP, height, width, thisSlider));
							}
						}
						else {
							if (sliderListener !== null) {
								sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.CLOSE_RUNNING, height, width, thisSlider));
							}
						}
					}
				}
			};

			/**
			 * Adds the instance of {@link SliderTimelineListener} to listen the timeline's events
			 * and operate the Slider's movements.
			 */
			timeline.addTimelineListener(sliderTimelineListener);

			/**
			 * Method that adds a SliderListener to this Slider.
			 * @param _sliderListener The sliderListener to give to this instance.
			 * @throws If _sliderListener is undefined.
			 * @throws If _sliderListener is null.
			 * @throws If _sliderListener is not an instance of SliderListener.
			 * FIXME [ygally] ajouter possibilité d'utiliser plusieurs listeners
			 */
			this.addSliderListener = function(_sliderListener) {
				if (!_sliderListener) {throw new Error('sliderListener is not valid');}
				if (typeof(_sliderListener.throwSliderEvent) !== 'function') {throw new Error('sliderListener does not implement #throwSliderEvent(SliderEvent) method');}
				sliderListener = _sliderListener;
				sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.INIT_LISTENER, height, width, thisSlider));
			};

			/**
			 * This method opens the sliding block (if not already opened).
			 */
			this.open = function() {
				if (direction !== Direction.OPEN && sliderListener !== null) {
					sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.OPEN_START, height, width, thisSlider));
				}
				direction = Direction.OPEN;
				//FIXME [ygally] demarrage dans SliderManager
				if (!timeline.isRunning()) {
					timeline.start();
				}
			};

			/**
			 * This method closes the sliding block (if not already closed).
			 */
			this.close = function() {
				if (direction !== Direction.CLOSE && sliderListener !== null) {
					sliderListener.throwSliderEvent(new ev.effects.SliderEvent(ev.effects.SliderEventType.CLOSE_START, height, width, thisSlider));
				}
				direction = Direction.CLOSE;
				//FIXME [ygally] demarrage dans SliderManager
				if (!timeline.isRunning()) {
					timeline.start();
				}
			};

			/**
			 * Slides the block, i.e:
			 * if the block is closed or is closing, opens it,
			 * if the block is opened or is opening, closes it.
			 */
			this.slide = function() {
				if (direction === Direction.NONE) {
					if ((minHeight !== null && maxHeight !== null && height <= minHeight) || (minWidth !== null && maxWidth !== null && width <= minWidth)) {
						this.open();
					}
					else if ((minHeight !== null && maxHeight !== null && height >= maxHeight) || (minWidth !== null && maxWidth !== null && width >= maxWidth)) {
						this.close();
					}
					else {// weird behaviour => open the slider
						this.open();
					}
				}
				else if (direction === Direction.CLOSE) {
					this.open();
				}
				else {
					this.close();
				}
			};

			this.toString = function() {
				return 'ev.effects.Slider{paneNode=' + _paneNode + ',delay=' + _delay + '}';
			};
		};
	}());// Exécution de la fonction anonyme

	/**
	 * SliderEvent's constructor.
	 *
	 * Defines the event produced by a Slider instance when it invokes
	 * SliderListener#throwSliderEvent(). This event contains its occurring date, the
	 * height (can be undefined if block is not sliding vertically), the width (same
	 * as height), the source slider, and the type of event. Event type is a
	 * constant read from {@link SliderEventType}:
	 * INIT_LISTENER when the listener is added to the slider,
	 * OPEN_START when the slider is asked to open,
	 * OPEN_RUNNING when the slider is sliding open,
	 * OPEN_STOP when the slider is ending its opening process,
	 * CLOSE_START when the slider is asked to close,
	 * CLOSE_RUNNING when the slider is sliding close,
	 * CLOSE_STOP when the slider is ending its closing process.
	 * @param _type Instance of SliderEventType holding this SliderEvent's type.
	 * @param _source The Slider instance who produced this event.
	 * @throws if _type is undefined.
	 * @throws if _type is null.
	 * @throws if _type is not a number.
	 * @throws if _height is defined but not a number.
	 * @throws if _width is defined but not a number.
	 * @throws if _source is undefined.
	 * @throws if _source is null.
	 * @throws if _source is not a Slider.
	 */
	ev.effects.SliderEvent = function(_type,_height,_width,_source) {
		if (typeof(_type) !== 'number') {throw new Error('type is not a number');}
		if (!ev.effects.SliderEventType.isValid(_type)) {throw new Error('type is not valid');}
		if (_height !== undefined && _height !== null && typeof(_height) !== 'number') {throw new Error('height is not a number');}
		if (_width !== undefined && _width !== null && typeof(_width) !== 'number') {throw new Error('width is not a number');}
		if (!_source) {throw new Error('source is not valid');}
		if (!(_source instanceof ev.effects.Slider)) {throw new Error('source is not instance of Timeline');}
		var date = new Date();

		/**
		 * Gets the type of the event one of the SliderEventType class constants.
		 */
		this.getType = function() {
			return _type;
		};

		/**
		 * Gets the date of occurrence.
		 */
		this.getDate = function() {
			return date;
		};

		/**
		 * Gets the height of the sliding block at the time of the event. undefined
		 * if the block does not slide vertically.
		 */
		this.getHeight = function() {
			return _height;
		};

		/**
		 * Gets the width of the sliding block at the time of the event. undefined
		 * if the block does not slide horizontally.
		 */
		this.getWidth = function() {
			return _width;
		};

		/**
		 * The slider interface which throws the event.
		 */
		this.getSource = function() {
			return _source;
		};

		this.toString = function() {
			return 'ev.effects.SliderEvent{type=' + ev.effects.SliderEventType.toString(_type) + ', date=' + date + ', height=' + _height + ', width=' + _width + ', source=' + _source + '}';
		};
	};

	/**
	 * SliderEventType enumeration.
	 *
	 * This enumeration contains each slider's event type.
	 */
	ev.effects.SliderEventType = {
		INIT_LISTENER: 0,
		OPEN_START: 1,
		OPEN_RUNNING: 2,
		OPEN_STOP: 3,
		CLOSE_START: 4,
		CLOSE_RUNNING: 5,
		CLOSE_STOP: 6,
		/**
		 * A method that returns whether the given event type is valid or not.
		 * @return a string.
		 */
		isValid: function(value) {
			switch (value) {
				case this.INIT_LISTENER: return !0;
				case this.OPEN_START: return !0;
				case this.OPEN_RUNNING: return !0;
				case this.OPEN_STOP: return !0;
				case this.CLOSE_START: return !0;
				case this.CLOSE_RUNNING: return !0;
				case this.CLOSE_STOP: return !0;
				default: return !1;
			}
		},
		/**
		 * A method that returns the name of the given event type.
		 * @return a string.
		 */
		toString: function(value) {
			switch (value) {
				case this.INIT_LISTENER: return 'INIT_LISTENER';
				case this.OPEN_START: return 'OPEN_START';
				case this.OPEN_RUNNING: return 'OPEN_RUNNING';
				case this.OPEN_STOP: return 'OPEN_STOP';
				case this.CLOSE_START: return 'CLOSE_START';
				case this.CLOSE_RUNNING: return 'CLOSE_RUNNING';
				case this.CLOSE_STOP: return 'CLOSE_STOP';
				default: return 'n.c';
			}
		}
	};

	/**
	 * FIXME : ceci assure seulement la compatibilité
	 */
	window.SliderESV = ev.effects.Slider;

	/**
	 * FIXME : ceci assure seulement la compatibilité
	 */
	window.SliderListener = function() {
		this.throwTimelineEvent = function() {
			throw new Error('#throwTimelineEvent() has not been implemented');
		};
		this.throwSliderEvent = function(e) {
			this.throwTimelineEvent(e);
		};
	};

	/**
	 * FIXME : ceci assure seulement la compatibilité
	 */
	window.SliderEvent = ev.effects.SliderEvent;

	/**
	 * FIXME : ceci assure seulement la compatibilité
	 */
	window.SliderEventType = function() {
		return ev.effects.SliderEventType;
	};
}());

