/**
 *  ****************************************************************************
 *  File : design/previmer/javascript/previmer_ihmmanager.js
 *  vers : 1.1
 *  build: 20070731
 *  Author : Virtualys
 *  ****************************************************************************
 **/

/**
 * ============================================================================= 
 * PrevimerIHMManager
 * Gestionnaire de l'interface Previmer
 * ============================================================================= 
 */
PrevimerIHMManager = function(browser) {
   this._components = {
      'COMBO_VARIABLES'       : $('.variables > .select'),
      'COMBO_AREAS'           : $('.areas > .select'),
      'COMBO_GEOITEMS'        : $('.geoitems > .select'),
      'COMBO_DEPTH'           : $('.depth > .select'),
      'COMBO_HOURS'           : $('.hours_select > .select'),
      'COMBO_FORMATS'         : $('.format > .select'),
      'CONTAIN_ANIM_DATES'    : $('.anim_dates'),
      'CONTAIN_IMAGE_MAPS'    : $('.areas_map .image'),
      'CONTAIN_ANIM_LINKS'    : $('.animation > .duration_commands'),
      'CONTAIN_ANIM_BUTS'     : $('.animation > .animation_commands'),
      'CONTAIN_ANIM_WAIT'     : $('.animation > .animation_progress'),
      'PROGRESS_LABEL'        : $('.animation > .animation_progress > .background > .label'),
      'PROGRESS_BAR'          : $('.animation > .animation_progress > .background > .progress'),
      'CONTAIN_RTYPE_BUTTONS' : $('.visutypes'),
      'CONTAIN_IMG_MAP_PFX'   : 'containImageMap-',
      'IMAGE_MAP_PFX'         : 'imageMap-',
      'IMAGE_PFX'             : 'image-',
      'FLASH'                 : ( $('#fla_embed_colormap').length > 0
                                    ? $('#fla_embed_colormap')
                                    : $('#fla_object_colormap') ),
      'CONTAIN_IMG_RESULT'    : $('.result'),
      'IMAGE_RESULT'          : $('.graphics img'),
      'BUTTON_UP_AREA'        : $('.areas_map > .button.up_area'),
      'BUTTON_ANIM_LOOP'      : $('.animation > .animation_commands').find('.loop'),
      'BUTTON_ANIM_PLAY'      : $('.animation > .animation_commands').find('.play'),
      'BUTTON_ANIM_STOP'      : $('.animation > .animation_commands').find('.stop'),
      'BUTTON_ANIM_SPEEDM'    : $('.animation > .animation_commands').find('.speed_less'),
      'BUTTON_ANIM_SPEEDP'    : $('.animation > .animation_commands').find('.speed_more'),
      'BUTTON_ANIM_IMG_PREV'  : $('.animation > .animation_commands').find('.img_prev'),
      'BUTTON_ANIM_IMG_NEXT'  : $('.animation > .animation_commands').find('.img_next'),
      'LINKS_PERMALINK'       : $('.plink > a'),
      'LINKS_PERMALINK_T'     : $('.plink_date > a'),
      'LINKS_PRINT'           : $('.print > a'),
      'LINKS_FR'              : $('.lang_fre-FR > a'),
      'LINKS_EN'              : $('.lang_eng-GB > a'),
      'LINKS_QUICK_ACCESS'    : $('div.resultcontainer > .tabs > .tab.related'),
      'LINKS_ALL_QUICK_ACCESS'   : $('div.resultcontainer > .tabs > .tab'),
      'LINKS_IMAGEMODE'          : $('.mode.image'),
      'LINKS_FLASHMODE'          : $('.mode.flash'),
/*      'LINK_SUBSCRIPTION'        : $('.picturesubscription > a'),*/
      'USER_REDIRECT_URI'        : $('#UserRedirectUri'),
      'BUTTON_PREV_DAY'          : $('.hours > .calendar').find('.headrow > td:first > a'),
      'BUTTON_NEXT_DAY'          : $('.hours > .calendar').find('.headrow > td:last > a'),
      'BUTTON_PREV_HOUR'         : $('.hours > .calendar').find('.headrow > td:eq(1) > a'),
      'BUTTON_NEXT_HOUR'         : $('.hours > .calendar').find('.headrow > td:eq(3) > a'),
      'BUTTON_OTHER_AREAS'       : $('.tp_areas > .title > .incrust > a'),
      'HOUR_GROUP_BUTTON'        : $('div.hours'),
      'BUTTON_SAVE'              : $('.save_commands > .command').find('a'),
      'BUTTON_POPUP'             : $('.display_commands').find('.popup > a'),
      'BUTTON_HELP'              : $('.help'),
      'BUTTON_ZOOMIN'            : $('.zoom_in > a'),
      'BUTTON_ZOOMOUT'           : $('.zoom_out > a'),
      'BUTTON_ZOOMFIT'           : $('.zoom_fit > a'),
      'BUTTON_ZOOM100'           : $('.zoom_100 > a'),
      'CONTAIN_TABS_INFOS'       : $('.result > .infos')
   };
	this._browser = browser;
	this._animDates = null;
	this._animLoadingMsg = 'Image ';
	this._ieTimeoutParams = null;

	// Gestion Flash
	this._flashPreviousParams = null;
	this._flashInit = false;
	this._flashTest = 20;
	this._isFlashInstalled = false;
	this._imageWaiting = null;
	this._flashVersion = null;  
	if ( window.ActiveXObject ) {  
		var control = null;  
		try {  
			control = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');  
		} catch (e) {  
			return;  
		}  
		if (control) {
			this._isFlashInstalled = true;
			this._flashVersion = control.GetVariable('$version').substring(4);  
			this._flashVersion = this._flashVersion.split(',');  
			this._flashVersion = parseFloat(this._flashVersion[0] + '.' + this._flashVersion[1]);
		}
	} else {
		// Check navigator.plugins for "Shockwave Flash"
		if ( navigator.mimeTypes &&
				navigator.mimeTypes["application/x-shockwave-flash"] &&
				navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin &&
				navigator.plugins &&
				navigator.plugins["Shockwave Flash"] )
		{
			//Flash is available
			this._isFlashInstalled = true;
		}else
		{
			//Flash is not available
			this._isFlashInstalled = false;
		} 
	}  
	if ( !this._isFlashInstalled )
	{
		window.location = this._browser._config.appURL + this._browser._state.getPermaLink(true) + '/(type)/image' + this._browser._config.URLSuffix;
	}
	   
	// Gestion du zoom et des dimensions d'image
	this._zoomLevel = 1.0;
	this._resultImgWidth = 0;
	this._resultImgHeight = 0;

	// Chargement des liens d'accès rapide aux thèmes
	if (this._components['LINKS_QUICK_ACCESS'] != null)
	{
		this._rapidThemeUrl = [];
		var nodes = this._components['LINKS_QUICK_ACCESS'].find('a');
		for (var i = 0; i < nodes.length ; ++i)
		{
			this._rapidThemeUrl.push({
				'node' : nodes[i],
				'baseUrl' : nodes[i].href.substring(0, nodes[i].href.length - 7) });
		}
		$(this._components['LINKS_ALL_QUICK_ACCESS'])
			.hover(
				function(){
					$(this).addClass('hovered');
				},
				function(){
					$(this).removeClass('hovered');
				})
			.click(
				function(){
					var link = $(this).find("a").attr("href");
					if ( link )
						window.location=link;
				});
	}

	// On déclare l'objet dans les instances globales
	this.className = 'PrevimerIHMManager';
	PrevimerToolkit.registerGlobalInstance(this);
}
PrevimerIHMManager.VALID_RESULT_TYPES = [
	[
		'map', {
			'fr' : [ 'Carte', 'Carte 2D' ],
			'en' : [ 'Map', '2D map' ] }
	],
	[
		'temporal', {
			'fr' : [ 'Courbe', 'Courbe temporelle' ],
			'en' : [ 'Curve', 'Temporal curve' ] }
	],
	[
		'spectrum', {
			'fr' : [ 'Spectre', 'Spectre' ],
			'en' : [ 'Spectrum', 'Spectrum' ] }
	],
	[
		'section', {
			'fr' : [ 'Section', 'Section verticale' ],
			'en' : [ 'Section', 'Vertical section' ] }
	],
	[
		'profile', {
			'fr' : [ 'Profil', 'Profil vertical' ],
			'en' : [ 'Profile', 'Vertical profile' ] }
	]
];
PrevimerIHMManager.prototype = {
/**
 * -----------------------------------------------------------------------------
 * Initialise l'interface.
 */ 
	'initIHM' : function()
	{
		// Initialisation des composants HTML
		this._loadVariablesCombo();
		this._loadAreasCombo();
		this._updateAreaMap();
		this._loadDepthCombo();
		this._loadResultTypes();
		this._loadGeoitemCombo();
		this._components['CALENDAR'] = this._browser._config.calendar;
		var _state = this._browser._state;
		this._components['CALENDAR'].setDisabledHandler(
			function(date) {
				var time = date.getTime();
				var timeConfig = _state.resultType.getTimeConfig();
				if (timeConfig.from != null) {
					return ((time < timeConfig.from) || (time > timeConfig.calendarTo));
				}
				return time > timeConfig.calendarTo;
			});
		this._components['CALENDAR'].setDate(new Date(this._browser._state.date.getTime()));
		this._loadHoursCombo();
		this._loadAnimationPane();
		if ( this._browser._animManager._loop && !this._components['BUTTON_ANIM_LOOP'].find('a').hasClass( 'selected' ))
		{
			this._components['BUTTON_ANIM_LOOP'].find('a').addClass( 'selected');
		}
		this._loadResultImage();
		this._browser._helpTopics.displayTopic('variable', this._browser._state, this._browser._config.locale);
		this._browser._helpTopics.displayTopic('emprise', this._browser._state, this._browser._config.locale);
		this._browser._helpTopics.displayTopic('visualisation_type', this._browser._state, this._browser._config.locale);
		this._browser._helpTopics.displayTopic('bulletin', this._browser._state, this._browser._config.locale);
		this._browser._helpTopics.displayTopic('analyse', this._browser._state, this._browser._config.locale);
		this._loadFormatCombo();

		// Initialisation des écouteurs d'événnements
		this._components['COMBO_VARIABLES'].change(function(){
			$(window.browser).get(0).getCBManager().onSelectVariable(this.selValue);
			return false;
		});
		this._components['COMBO_DEPTH'].change(function(){
			$(window.browser).get(0).getCBManager().onSelectDepth(this.selValue);
			return false;
		});
		this._components['COMBO_GEOITEMS'].change(function(){
			$(window.browser).get(0).getCBManager().onSelectGeoItem(this.selValue);
			return false;
		});
		this._components['COMBO_AREAS'].change(function(){
			$(window.browser).get(0).getCBManager().onSelectArea(this.selValue);
			return false;
		});
		this._components['BUTTON_PREV_DAY'].click(function(){
			$(window.browser).get(0).getCBManager().onChangeDay(-1);
			return false;
		});
		this._components['BUTTON_NEXT_DAY'].click(function(){
			$(window.browser).get(0).getCBManager().onChangeDay(1);
			return false;
		});
		this._components['BUTTON_PREV_HOUR'].click(function(){
			$(window.browser).get(0).getCBManager().onChangeHour(-1);
			return false;
		});
		this._components['BUTTON_NEXT_HOUR'].click(function(){
			$(window.browser).get(0).getCBManager().onChangeHour(1);
			return false;
		});
		this._components['COMBO_HOURS'].change(function(){
			$(window.browser).get(0).getCBManager().onSelectHour(this.selValue);
			return false;
		});
		this._components['BUTTON_ANIM_PLAY'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onAnimPlay();
			return false;
		});
		this._components['BUTTON_ANIM_STOP'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onAnimStop();
			return false;
		});
		this._components['BUTTON_ANIM_SPEEDM'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onAnimSpeed(-1);
			return false;
		});
		this._components['BUTTON_ANIM_SPEEDP'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onAnimSpeed(1);
			return false;
		});
		this._components['BUTTON_ANIM_IMG_PREV'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onChangeHour(-1);
			return false;
		});
		this._components['BUTTON_ANIM_IMG_NEXT'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onChangeHour(1);
			return false;
		});
		this._components['BUTTON_ANIM_LOOP'].find('a').click(function(){
			if( !$(this).hasClass('disabled'))
			{
				$(this).toggleClass('selected');
				$(window.browser).get(0).getCBManager().onAnimToggleLoop();
			}
			return false;
		});
		this._components['BUTTON_SAVE'].click(function(){
			if ( !$(this).hasClass( 'disabled') )
				$(window.browser).get(0).getCBManager().onSaveImage();
			return false;
		});
		this._components['BUTTON_POPUP'].click(function(){
			if ( !$(this).hasClass( 'disabled') )
				$(window.browser).get(0).getCBManager().onPopup();
			return false;
		});
		this._components['BUTTON_ZOOMIN'].click(function(){
			if ( !$(this).hasClass('disabled') )
				$(window.browser).get(0).getCBManager().onZoomIn();
			return false;
		});
		this._components['BUTTON_ZOOMOUT'].click(function(){
			if ( !$(this).hasClass('disabled') )
				$(window.browser).get(0).getCBManager().onZoomOut();
			return false;
		});
		this._components['BUTTON_ZOOMFIT'].click(function(){
			if ( !$(this).hasClass('disabled') )
				$(window.browser).get(0).getCBManager().onZoomFit();
			return false;
		});
		this._components['BUTTON_ZOOM100'].click(function(){
			if ( !$(this).hasClass('disabled') )
				$(window.browser).get(0).getCBManager().onZoom100();
			return false;
		});
		this._components['BUTTON_UP_AREA'].find('a').click(function(){
			if (!$(this).hasClass('disabled'))
				$(window.browser).get(0).getCBManager().onUpArea();
			return false;
		});

		this._components['PROGRESS_LABEL'].html( this._animLoadingMsg + '0/1');
		this._components['PROGRESS_BAR'].css( 'width', '0%' );

		this._syncAnimDates();
	},	
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une variable dans la
 * liste déroulante.
 * @param variableIdx la variable dans la liste déroulante 
 */ 
	'onSelectVariable' : function(variable)
	{
		var previousState = this._browser._state.copy();
		this._resetAnimation();
		this._browser._state.selectVariable(variable);
		// Lors du changement de variable, on recharge la liste des types de
		// résultat et la liste des profondeurs
		this._loadResultTypes();
		if (this._browser._state.resultType.id != 'map' ||
				previousState.resultType.id != this._browser._state.resultType.id)
		{
			this._updateAreaMap();
		}
		// On recherche la liste des variables en cas de changement de type de résultat
		this._loadVariablesCombo();
		this._loadDepthCombo();
		this._loadGeoitemCombo();
		this._components['CALENDAR'].refresh();
		this._loadHoursCombo();
		this._loadResultImage();
		this._loadAnimationPane();
		// Chargement des aides
		if (this._browser._state.resultType.id != previousState.resultType.id)
		{
			this._browser._helpTopics.displayTopic('visualisation_type', this._browser._state, this._browser._config.locale);
		}
		if (this._browser._state.area._id != previousState.area._id)
		{
			this._browser._helpTopics.displayTopic('emprise', this._browser._state, this._browser._config.locale);
		}
		this._browser._helpTopics.displayTopic('variable', this._browser._state, this._browser._config.locale);
		this._loadFormatCombo();
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'un type de résultat
 * dans la liste déroulante ou via un bouton.
 * @param rtypeIdx l'index du type de résultat dans la liste déroulante 
 */ 
	'onSelectResultType' : function(rtypeIdx)
	{
		this._resetAnimation();
		var rTypeID = rtypeIdx;
		if (this._browser._state.variable.resultTypes[rTypeID])
		{
			var oldRTypeID = this._browser._state.resultType.id;
			this._browser._state.selectResultType(rTypeID);

			this._loadVariablesCombo();
			// Lors du changement de types de résultat, on recharge la liste des
			// profondeurs
			this._loadDepthCombo();
			this._syncAreasCombo(this._browser._state.area._id);

			// On synchronise les boutons de types de résultats
			var rTypeButtons = this._components['CONTAIN_RTYPE_BUTTONS'];
			rTypeButtons.find('.visutype.' + rTypeID ).find('a').addClass('selected');
			rTypeButtons.find('.visutype.' + oldRTypeID ).find('a').removeClass('selected');

			// Mise à jour de l'IHM
			this._updateAreaMap();
			this._components['CALENDAR'].refresh();
			this._loadHoursCombo();
			this._loadGeoitemCombo();
			this._loadResultImage();
			this._loadAnimationPane();
			this._browser._helpTopics.displayTopic('visualisation_type', this._browser._state, this._browser._config.locale);
			this._loadFormatCombo();
			this._syncAnimDates();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une profondeur
 * dans la liste déroulante.
 * @param depth la profondeur dans la liste déroulante 
 */ 
	'onSelectDepth' : function(depth)
	{
		this._resetAnimation();
		this._browser._state.selectDepth(depth);
		this._loadResultImage();
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une emprise
 * dans la liste déroulante ou sur la carte 2D.
 * @param areaIdx l'index de l'emprise dans la liste déroulante
 * @param syncCombo true pour resynchroniser l'élément sélectionné dans la liste
 * déroulante (utile lors d'un clic depuis la carte) 
 */ 
	'onSelectArea' : function(areaIdx, syncCombo)
	{
		this._resetAnimation();
		var areaID = areaIdx;//this._components['COMBO_AREAS'].get(0).selValue;
		this._browser._state.selectArea(areaID);
		this._loadGeoitemCombo();
		// Lors du changement d'emprise, on recharge la carte des emprises
		if (syncCombo)
		{
			this._syncAreasCombo(areaID);
		}
		this._updateAreaMap();
		this._loadResultImage();
		this._browser._helpTopics.displayTopic('emprise', this._browser._state, this._browser._config.locale);
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors du clic sur le bouton de retour à l'emprise
 * parente. 
 */ 
	'onUpArea' : function() {
		this._resetAnimation();
		var areaID = this._browser._state.area._area;
		if (areaID)
		{
			this._browser._state.selectArea(areaID);
			// Lors du changement d'emprise, on recharge la carte des emprises, et on
			// sélectionne le bon élément dans la liste des emprises
			this._syncAreasCombo(areaID);
			this._updateAreaMap();
			this._loadGeoitemCombo();
			this._loadResultImage();
			this._browser._helpTopics.displayTopic('emprise', this._browser._state, this._browser._config.locale);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'un item géométrique
 * sur la carte 2D.
 * @param areaID l'identifant de l'emprise
 * @param geoItemID l'identifiant de l'élément
 */ 
	'onSelectGeoItem' : function(geoItemID, syncCombo) {
		this._resetAnimation();
		this._browser._state.selectGeoItem(geoItemID);
		if (syncCombo)
		{
			this._syncGeoitemCombo(geoItemID);
		}
		this._updateAreaMap();
		this._loadResultImage();
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une heure
 * dans la liste déroulante.
 * @param hourIdx l'index de l'heure dans la liste déroulante
 */ 
	'onSelectHour' : function(hourIdx) {
		this._browser._state.selectHour(
			this._components['COMBO_HOURS'].get(0).selValue);
		this._loadResultImage();
		this._syncAnimDates();
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une heure
 * via les boutons flèches.
 * @param dir -1 pour revenir 1 pas en arrière, +1 pour 1 pas en avant
 */ 
	'onChangeHour' : function(dir)
	{
		if (this._browser._animManager.isRunning())
		{
			// Animation en cours : on modifie le pas en cours de l'animation
			if (this._browser._animManager.isPaused())
			{
				// Ne fonctionne qu'en mode pause
				if (dir == -1)
				{
					if (this._browser._config.displaymode == 'image')
						this._browser._animManager.prevStep();
					else
						this._callFlashCommand('prev', null);
				}else
				{
					if (this._browser._config.displaymode == 'image')
						this._browser._animManager.nextStep();
					else
						this._callFlashCommand('next', null);
				}
			}
		}else
		{
			var timeConfig = this._browser._state.resultType.getTimeConfig();
			if (timeConfig.unit == 'hour' || timeConfig.unit == 'minute')
			{
				// On modifie l'heure sélectionnée
				var combo = this._components['COMBO_HOURS'];
				var index = combo.get(0).dropdown.find('a').index(combo.get(0).selItem);

				if (dir == -1 && index == 0)
				{
					// Cas particulier : on doit passer au jour précédent
					var date = new Date(this._browser._state.date.getTime());
					date.setUTCDate(date.getUTCDate() - 1);
					if ( this._browser._state.date.getTimezoneOffset() != date.getTimezoneOffset() )
					   date.setUTCHours(date.getUTCHours() + ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) / 60 ) );
					if (this._components['CALENDAR'].getDateStatus(date)) return;
					this._browser._state.selectDate(date);
					this._components['CALENDAR'].setDate(date);
					this._loadHoursCombo();
					$.vSelect.select(combo, ':last');
				}
				else if (dir == 1 && index == combo.get(0).dropdown.find('a').length - 1)
				{
					// Cas particulier : on doit passer au jour suivant
					var date = new Date(this._browser._state.date);
					date.setUTCDate(date.getUTCDate() + 1);
					if ( this._browser._state.date.getTimezoneOffset() != date.getTimezoneOffset() )
						date.setUTCHours(date.getUTCHours() + ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) / 60 ) );
					if (this._components['CALENDAR'].getDateStatus(date)) return;
					this._browser._state.selectDate(date);
					this._components['CALENDAR'].setDate(date);
					this._loadHoursCombo();
					$.vSelect.select(combo, ':first');
				}
				else
				{
					$.vSelect.select(combo, dir == 1 ? ':next' : ':prev');
				}
				this._browser._state.selectHour(combo.get(0).selValue);
			}
			else
			{
				// On change de jour
				var date = new Date(this._browser._state.date.getTime());
				date.setUTCDate(date.getUTCDate() + dir);
				if ( this._browser._state.date.getTimezoneOffset() != date.getTimezoneOffset() )
					date.setUTCHours(date.getUTCHours() + ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) / 60 ) );
				if (this._components['CALENDAR'].getDateStatus(date)) return;
				this._browser._state.selectDate(date);
				this._components['CALENDAR'].setDate(date);
			}
			this._loadResultImage();
			this._browser._helpTopics.displayTopic('bulletin', this._browser._state, this._browser._config.locale);
			this._browser._helpTopics.displayTopic('analyse', this._browser._state, this._browser._config.locale);
			this._syncAnimDates();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'un jour
 * via les boutons flèches.
 * @param dir -1 pour revenir 1 jour en arrière, +1 pour 1 jour en avant
 */
	'onChangeDay' : function(dir)
	{
		if (!this._browser._animManager.isRunning())
		{
			// On change de jour
			var date = new Date(this._browser._state.date.getTime());
			date.setUTCDate(date.getUTCDate() + dir);
			if ( this._browser._state.date.getTimezoneOffset() != date.getTimezoneOffset() )
			{
				date.setUTCHours(date.getUTCHours() + ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) / 60 ) );
				this._browser._state.hourOffset = this._browser._state.hourOffset - ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) * 60000 );
			}
			if (this._components['CALENDAR'].getDateStatus(date)) return;
			this._browser._state.selectDate(date);
			this._components['CALENDAR'].setDate(date);
			this._loadHoursCombo();
			var timeConfig = this._browser._state.resultType.getTimeConfig();
			if (timeConfig.unit == 'hour' || timeConfig.unit == 'minute')
				this._syncHoursCombo(this._browser._state.hourOffset);
			this._loadResultImage();
			this._browser._helpTopics.displayTopic('bulletin', this._browser._state, this._browser._config.locale);
			this._browser._helpTopics.displayTopic('analyse', this._browser._state, this._browser._config.locale);
			this._syncAnimDates();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la sélection d'une date dans le
 * calendrier.
 * @param date la date sélectionnée
 */ 
	'onDateChange' : function(date)
	{
		this._resetAnimation();
		if ( this._browser._state.date.getTimezoneOffset() != date.getTimezoneOffset() )
			this._browser._state.hourOffset = this._browser._state.hourOffset - ( ( date.getTimezoneOffset() - this._browser._state.date.getTimezoneOffset() ) * 60000 );
		this._browser._state.selectDate(date);
		this._loadHoursCombo();
		var cal = this._components['CALENDAR'];
		var eventTarget = null;
		if ((cal != null) && (cal.lastEvent != null))
			eventTaget = Calendar.getTargetElement(cal.lastEvent);
		if ((eventTaget != null) &&
				(typeof eventTaget.navtype != 'undefined') &&
				(eventTaget.navtype == 0))
		{
			var date = new Date();
			var newOffset = date.getHours() * 3600000 + date.getMinutes() * 60000 + date.getSeconds() * 1000;
			this._browser._state.selectHour(newOffset);
			this._browser._state._synchronizeHours();
		}
		var timeConfig = this._browser._state.resultType.getTimeConfig();
		if (timeConfig.unit == 'hour' || timeConfig.unit == 'minute')
			this._syncHoursCombo(this._browser._state.hourOffset);
		this._loadResultImage();
		this._browser._helpTopics.displayTopic('bulletin', this._browser._state, this._browser._config.locale);
		this._browser._helpTopics.displayTopic('analyse', this._browser._state, this._browser._config.locale);
		this._syncAnimDates();
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande d'un zoom avant sur l'image.
 */ 
	'onZoomIn': function()
	{
		if (this._browser._config.displaymode == 'image')
		{
			this._zoomLevel = Math.min(4, Math.floor((this._zoomLevel * 10) + 2.5) / 10);
			this._changeImgSize();
		}
		else
		{
			this._callFlashCommand('zoomIn', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande d'un zoom arrière sur l'image.
 */ 
	'onZoomOut': function() {
		if (this._browser._config.displaymode == 'image') {
			this._zoomLevel = Math.max(1, Math.floor((this._zoomLevel * 10) - 2.5) / 10);
			this._changeImgSize();
		}
		else {
			this._callFlashCommand('zoomOut', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande d'un zoom arrière sur l'image
 * permettant à celle ci de remplir le conteneur.
 */ 
	'onZoomFit': function() {
		if (this._browser._config.displaymode == 'image')
		{
			var scrollPane = this._components['CONTAIN_IMG_RESULT'];
			this._zoomLevel =	Math.min(
					(scrollPane.attr('offsetWidth') - 8) / this._resultImgWidth,
					(scrollPane.attr('offsetHeight') - 8) / this._resultImgHeight);
			this._changeImgSize();
		}
		else
		{
			this._callFlashCommand('zoomFit', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande d'un retour au zoom initial.
 */ 
	'onZoom100': function()
	{
		if (this._browser._config.displaymode == 'image') {
			this._zoomLevel = 1.0;
			this._changeImgSize();
		}
		else {
			this._callFlashCommand('zoom100', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande de sauvegarde de l'image en
 * cours d'affichage.
 */ 
	'onSaveImage': function() {
		var combo = this._components['COMBO_FORMATS'];
		if (this._browser._config.ajaxProxy)
		{
			window.location =
				PrevimerToolkit.getResultRequest(
						this._browser._state.getRequestParams(
								CurrentState.TYPE_RESULT, this._browser._config),
						this._browser._config) +
				'%26format%3D' + combo.get(0).selValue + '%26download%3Dtrue';
		}
		else
		{
			window.location =
				PrevimerToolkit.getResultRequest(
						this._browser._state.getRequestParams(
								CurrentState.TYPE_RESULT, this._browser._config),
						this._browser._config) +
				'/format/' + combo.get(0).selValue + '/download/true';
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande du démarrage d'une animation.
 */ 
	'onAnimStart': function(animIdx) {
		var state = this._browser._state;
		state.selectAnimation(animIdx);

		// Chargement des URLs des images et des dates associées
		var URLs = [];
		if (this._browser._config.displaymode == 'image')
		{
			URLs = state.animation.getURLs(state, this._browser._config);
			var that = this;
			this._browser._animManager.setRunningMode( 'loading' );
			this._browser._animManager.load(
					URLs[0],
					function() {
						if (that._resultImgWidth != this.width ||
								that._resultImgHeight != this.height) {
							that._resultImgWidth = this.width;
							that._resultImgHeight = this.height;
//							that._changeImgSize();
						}
					},
					'PrevimerToolkit.getGlobalInstance("' + this._objectID + '").onAnimLoading',
					'PrevimerToolkit.getGlobalInstance("' + this._objectID + '").onAnimLoaded');
			this._animDates = URLs[1];
		}
		else
		{
			this._callFlashCommand('animStart', animIdx);
		}

		if ( URLs.length > 0 )
		{
			this._syncAnimDates( URLs[1][0], URLs[1][URLs[1].length - 1 ]);
		}else
		{
			var stepUnit = state.resultType.getTimeConfig().unit;
			if (stepUnit == 'hour' || stepUnit == 'minute')
			{
				var startDate = state.date.getTime() + state.hourOffset;
			}
			else
			{
				startDate = state.date.getTime() - state.date.getTimezoneOffset() * 60000;
			}
			var limits = state.animation.getLimits( startDate, state, false );
			this._syncAnimDates(limits.startDate, limits.endDate );
		}
		this._components['CONTAIN_ANIM_BUTS'].find('.button > a').addClass('disabled');
		this._components['CONTAIN_ANIM_LINKS'].find('.button > a').not('.selected').addClass('disabled');
		this._components['CONTAIN_ANIM_DATES'].find('.at').css('display', 'none');
		this._components['CONTAIN_ANIM_DATES'].find('.range').css('display', 'block');
		this._components['BUTTON_ZOOMIN'].addClass('disabled');
		this._components['BUTTON_ZOOMOUT'].addClass('disabled');
		this._components['BUTTON_ZOOMFIT'].addClass('disabled');
		this._components['BUTTON_ZOOM100'].addClass('disabled');
		this._components['LINKS_PRINT'].addClass('disabled');
		this._components['BUTTON_POPUP'].addClass('disabled');
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lorsqu'une animation a terminé son chargement.
 */ 
	'onAnimLoaded': function()
	{
		// Contrôle du nombre d'image chargée
		if ( ( this._browser._state.getFlashState() &&
				this._browser._state.getFlashState().anim.count > 0 ) ||
			this._browser._animManager._images.length > 0 )
		{
			// Affichage du bloc Animation
			this._components['CONTAIN_ANIM_BUTS'].find('.button > a').removeClass('disabled');
			this._components['CONTAIN_ANIM_LINKS'].find('.button > a').addClass('disabled');

			this._components['COMBO_HOURS'].attr('disabled', 'disabled');
			this._components['BUTTON_PREV_DAY'].addClass( 'disabled');
			this._components['BUTTON_NEXT_DAY'].addClass( 'disabled' );
			if (this._browser._config.displaymode == 'image')
			{
				this._changeImgSize();
				this._browser._animManager.play();
			}else
			{
				// changer l'état de l'anim manager à running
				this._browser._animManager.setRunningMode('play');
			}
			this._loadFormatCombo();
		}else
		{
			alert( this._browser._config.locale == 'en' ? "The animation contains no data.": "L'animation ne contient aucune donnée." );
			this.onAnimStop();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lorsqu'une animation a terminé le chargement
 * d'une image.
 */ 
	'onAnimLoading': function(param)
	{
//		this._components['CONTAIN_ANIM_WAIT'].find('.label').html(
//				this._animLoadingMsg + " " + param[0] + "/" + param[1] );
		this._components['PROGRESS_LABEL'].html(
				"Loading " + ( param[0] + 1 ) + "/" + param[1]);
		this._components['PROGRESS_BAR'].css( 'width',
				Math.round( ( param[0] + 1 ) / param[1] * 100 ) + '%' );

	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande d'arrêt d'une animation.
 */ 
	'onAnimStop': function()
	{
		if (this._browser._config.displaymode == 'image')
		{
			this._browser._animManager.stop();
		}
		else
		{
			this._callFlashCommand('animStop', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande de mise en play / pause d'une
 * animation.
 */ 
	'onAnimPlay': function()
	{
		this._components['BUTTON_ZOOMIN'].toggleClass('disabled');
		this._components['BUTTON_ZOOMOUT'].toggleClass('disabled');
		this._components['BUTTON_ZOOMFIT'].toggleClass('disabled');
		this._components['BUTTON_ZOOM100'].toggleClass('disabled');
		this._components['BUTTON_POPUP'].toggleClass('disabled');
		this._components['LINKS_PRINT'].toggleClass('disabled');
		if (this._browser._config.displaymode == 'image')
		{
			if (this._browser._animManager.isPaused())
			{
				this._browser._animManager.play();
			}
			else
			{
				this._browser._animManager.pause();
			}
		}
		else
		{
			this._callFlashCommand('animPlayPause', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée lors de la demande de modification de la vitesse
 * de défilement de l'animation .
 * @param speed -1 pour réduire la vitesse, +1 pour l'augmenter 
 */ 
	'onAnimSpeed' : function(speed)
	{
		if (this._browser._config.displaymode == 'image')
		{
			if (speed < 0)
			{
				this._browser._animManager.setSlower();
			}
			else
			{
				this._browser._animManager.setFaster();
			}
		}
		else
		{
			if (speed < 0)
			{
				this._callFlashCommand('animSlower', null);
			}
			else
			{
				this._callFlashCommand('animFaster', null);
			}
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, appelée permettant d'activer ou désactiver le loop
 * sur l'animation.
 */ 
	'onAnimToggleLoop' : function()
	{
		if (this._browser._config.displaymode == 'image')
		{
			this._browser._animManager.toggleLoop();
			this._components['BUTTON_ANIM_LOOP'].attr('class',
					this._browser._animManager._loop ? 'selectedButton' : 'enabledButton');
		}
		else
		{
			this._callFlashCommand('animLoop', null);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Callback appelée par le module d'animation après chaque affichage d'une
 * image.
 * @param params tableau contenant dans l'ordre :
 * [ objet image, index de l'image, gestionnaire d'animation ] 
 */ 
	'onNextImage' : function(params) {
		// Image suivante
		if (params[0] != null)
		{
		  this._components['IMAGE_RESULT'].attr('src', params[0].src);
		}

		// Mise à jour de la barre de progression
		this._components['PROGRESS_LABEL'].html( this._animLoadingMsg +
				(params[1] + 1) + '/' + params[2]._images.length );
		this._components['PROGRESS_BAR'].css('width',
				Math.round((((params[1] + 1.0) / params[2]._images.length) ) * 100) + '%');

/*
		// Synchronisation du calendrier
		var date = new Date(this._animDates[params[1]]);
		var calendar = this._components['CALENDAR'];
		if (date.getDate() != calendar.date.getDate()
				|| date.getMonth() != calendar.date.getMonth()
				|| date.getFullYear() != calendar.date.getFullYear())
		{
			calendar.setDate(date);
			this._loadHoursCombo(true);
		}
		
		// Synchronisation de la liste déroulante des heures
		var hourOffset =
			date.getHours() * 3600000 + date.getMinutes() * 60000 +
			date.getSeconds() * 1000;
		var comboHours = this._components['COMBO_HOURS'];
		this._syncHoursCombo(hourOffset);
*/
	},
	'onNextFlashImage' : function(params) {
		// Mise à jour de la barre de progression
		this._components['PROGRESS_LABEL'].html( this._animLoadingMsg +
			(params[0] + 1) + '/' + params[1]);
		this._components['PROGRESS_BAR'].css('width',
				Math.round((((params[0] + 1.0) / params[1])) * 100) + '%');
	},
/**
 * -----------------------------------------------------------------------------
 * Callback appelée par le module d'animation lors de l'arrêt de l'animation.
 */ 
	'onAnimStopped' : function(params) {
		// Fin de l'animation
		this._browser._state.selectAnimation(null);
		// Affiche l'image d'origine
		this._loadResultImage();
		this._loadFormatCombo();
		// Mise à jour de la barre de progression
		var progress = this._components['PROGRESS_BAR'];
		progress.css('backgroundPosition', '-184px');
		this._components['CONTAIN_ANIM_BUTS'].find('.button > a').addClass('disabled');
		this._components['CONTAIN_ANIM_LINKS'].find('.button > a').removeClass('disabled');
		this._components['CONTAIN_ANIM_LINKS'].find('.button > a').removeClass('selected');
		this._components['BUTTON_ZOOMIN'].removeClass('disabled');
		this._components['BUTTON_ZOOMOUT'].removeClass('disabled');
		this._components['BUTTON_ZOOMFIT'].removeClass('disabled');
		this._components['BUTTON_ZOOM100'].removeClass('disabled');
		this._components['LINKS_PRINT'].removeClass('disabled');
		this._components['BUTTON_POPUP'].removeClass('disabled');
		this._components['CONTAIN_ANIM_DATES'].find('.at').css('display', 'block');
		this._components['CONTAIN_ANIM_DATES'].find('.range').css('display', 'none');
	},
/**
 * -----------------------------------------------------------------------------
 * Callback javascript, affichant une popup d'un type particulier.
 * @param url si l'argument est présent, affiche dans la popup l'URL indiquée ;
 * sinon la ressource associée au format de données actuellement sélectionnée
 * dans la liste des formats est affichée 
 * Thierry a rajouté scrollbars=yes dans le cas d'une url existante 
 */ 
	'onPopup' : function(url)
	{
		if (url)
		{
			window.open(url, '_blank',
				'width=750,height=550,location=no,status=no,directories=no,toolbar=no,menubar=yes,resizable=yes,scrollbars=yes')
			.focus();
		}
		else
		{
			var url =
				PrevimerToolkit.getResultRequest(
						this._browser._state.getRequestParams(
								CurrentState.TYPE_RESULT, this._browser._config),
						this._browser._config);
			if (this._browser._config.ajaxProxy)
			{
				url += '%26format%3D';
			}
			else
			{
				url += '/format/';
			}
			url += 'image';
			var params = 'width=';
			if (this._browser._config.displaymode == 'flash')
			{
				params += '750,height=550';
			}
			else
			{
				params += (this._resultImgWidth + 20)
					+ ',height=' + (this._resultImgHeight + 20);
			}
			params += ',location=no,status=no,directories=no,toolbar=no,menubar=yes,resizable=yes';
			window.open(url, '_blank', params).focus();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Resynchronise l'élément actuellement sélectionné dans la liste déroulante des
 * emprises avec le choix de l'utilisateur.
 */ 
	'_syncAreasCombo' : function(areaID)
	{
		$.vSelect.select( this._components['COMBO_AREAS'], areaID );
	},
	'_syncGeoitemCombo' : function(geoIdx)
	{
		$.vSelect.select( this._components['COMBO_GEOITEMS'], geoIdx );
	},
/**
 * -----------------------------------------------------------------------------
 * Resynchronise l'élément actuellement sélectionné dans la liste déroulante des
 * heures avec le choix de l'utilisateur.
 */ 
	'_syncHoursCombo' : function(hourOffset)
	{
		$.vSelect.select( this._components['COMBO_HOURS'], hourOffset );
	},
/**
 *
 */
	'_syncAnimDates' : function(startDate, endDate)
	{
		var animDateContainer = this._components['CONTAIN_ANIM_DATES'];
		if ( startDate && endDate )
		{
			startDate = new Date( startDate );
			/*htmlStart += this._browser._state.resultType.getTimeConfig().getFormatedHour(
						this._browser._state.hourOffset,
						startDate )*/;
			animDateContainer.find( '.from > .date' ).html(
					'<div class="left"></div><div class="right"></div>' +
					(startDate.getDate() < 10 ? '0' : '' ) + startDate.getDate() + '/' +
					(startDate.getMonth() + 1 < 10 ? '0' : '' ) + ( startDate.getMonth() + 1 ) + '/' +
					startDate.getFullYear() + ' ' +
					(startDate.getHours() < 10 ? '0' : '' ) + startDate.getHours() + ':' +
					(startDate.getMinutes() < 10 ? '0' : '' ) + startDate.getMinutes() );

			endDate = new Date( endDate );
			/*htmlEnd += this._browser._state.resultType.getTimeConfig().getFormatedHour(
						this._browser._state.hourOffset,
						endDate );*/
			animDateContainer.find( '.to > .date' ).html( 
					'<div class="left"></div><div class="right"></div>' +
					(endDate.getDate() < 10 ? '0' : '' ) + endDate.getDate() + '/' +
					(endDate.getMonth() + 1 < 10 ? '0' : '') + ( endDate.getMonth() + 1 ) + '/' +
					endDate.getFullYear() + ' ' +
					(endDate.getHours() < 10 ? '0' : '' ) + endDate.getHours() + ':' +
					(endDate.getMinutes() < 10 ? '0' : '' ) + endDate.getMinutes() );
		}else
		{
			var date = this._browser._state.date;
			var htmlDate = '<div class="left"></div><div class="right"></div>';
			htmlDate += (date.getDate() < 10 ? '0' : '' ) + date.getDate() + '/';
			htmlDate += (date.getMonth() + 1 < 10 ? '0' : '' )+ ( date.getMonth() + 1 );
			htmlDate += '/';
			htmlDate += date.getFullYear() + ' ';
			htmlDate += this._browser._state.resultType.getTimeConfig().getFormatedHour(
						this._browser._state.hourOffset,
						date );
			animDateContainer.find( '.at > .date' ).html( htmlDate );
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Arrête l'animation en cours si une animation est en cours, et réinitialise
 * l'interface. Méthode à utiliser lors de l'interruption d'une animation par
 * la sélection d'un critère dans l'interface. 
 */ 
	'_resetAnimation' : function() {
		if (this._browser._animManager.isRunning()) {
			this._browser._animManager.stop();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Resynchronise l'image avec le facteur de zoom actuel.
 */ 
	'_changeImgSize': function() {
		var scrollPane = this._components['CONTAIN_IMG_RESULT'];
		var imgPane = this._components['IMAGE_RESULT'];
		var posLeft = (scrollPane.attr('scrollLeft') + scrollPane.attr('clientWidth') / 2) / scrollPane.attr('scrollWidth');
		var posTop = (scrollPane.attr('scrollTop') + scrollPane.attr('clientHeight') / 2) / scrollPane.attr('scrollHeight');
		imgPane.attr('width', Math.round(this._resultImgWidth * this._zoomLevel));
		imgPane.attr('height', Math.round(this._resultImgHeight * this._zoomLevel));
		scrollPane.attr('scrollLeft', Math.round(scrollPane.attr('scrollWidth') * posLeft - scrollPane.attr('clientWidth') / 2));
		scrollPane.attr('scrollTop', Math.round(scrollPane.attr('scrollHeight') * posTop - scrollPane.attr('clientHeight') / 2));
	},
/**
 * -----------------------------------------------------------------------------
 * Charge l'image résultat correspondant aux critères actuels.
 */ 
	'_loadResultImage' : function(forceParams) {
		var params = forceParams == undefined ?
				this._browser._state.getRequestParams(
						CurrentState.TYPE_RESULT, this._browser._config)
				: forceParams;

		if (this._imageLoading)
		{
			this._imageWaiting = params;
		}else
		{
			this._imageLoading = true;
			if (this._browser._config.displaymode == 'image')
			{
				var image = new Image();
				var that = this;
				image.onload = function(event)
				{
					var cbmanager = that._browser.getCBManager();
					cbmanager._components['IMAGE_RESULT'].attr('src', this.src);
					cbmanager._resultImgWidth = this.width;
					cbmanager._resultImgHeight = this.height;
					that._changeImgSize();
					that._imageLoading = false;
					if (that._imageWaiting != null)
					{
						var params = that._imageWaiting;
						that._imageWaiting = null;
						that._loadResultImage(params);
					}
					cbmanager._components['PROGRESS_LABEL'].html( '' );
					cbmanager._components['PROGRESS_BAR'].css( 'width', '0%' );
				};
				image.src = PrevimerToolkit.getResultRequest(params, this._browser._config);
			}else
			{
				params.nf404 = true;
				this._browser.getCBManager()._callFlashCommand('getResult', params);
			}
			this._components['PROGRESS_LABEL'].html( this._animLoadingMsg + '0/1');
			this._components['PROGRESS_BAR'].css( 'width',
					Math.round( 0 / 1* 100 ) + '%' );

			this._components['BUTTON_ZOOMIN'].removeClass('disabled');
			this._components['BUTTON_ZOOMOUT'].removeClass('disabled');
			this._components['BUTTON_ZOOMFIT'].removeClass('disabled');
			this._components['BUTTON_ZOOM100'].removeClass('disabled');
			this._components['BUTTON_SAVE'].removeClass('disabled');
			this._components['BUTTON_POPUP'].removeClass('disabled');
			this._loadPermalinks();
			this._browser._helpTopics.displayTopic('result_infos', this._browser._state, this._browser._config.locale);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix de variable.
 */ 
	'_loadVariablesCombo' : function()
	{
		// Chargement de la liste des variables
		var variables = this._browser._state.application.getVariables();
		var combo = this._components['COMBO_VARIABLES'];
		var variableOptions = new Array();
		for (var variable in variables)
		{
			var selected = this._browser._state.variable.id == variable ? 'selected="selected"' : '';
			variableOptions.push({
				value	:	variables[variable].id,
				text	:	variables[variable].label,
				level	:	0,
				selected:	selected,
				cssClass: variables[variable].resultTypes[this._browser._state.resultType.id] == null ? 'different' : ''
			});
		}
		combo.vSelect( variableOptions, {disabled: false} );
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix d'emprises.
 */ 
	'_loadAreasCombo' : function() {
		// Chargement de la liste des emprises
		var rootarea = this._browser._state.application.getRootArea();
		var combo = this._components['COMBO_AREAS'];
		var selectedArea = this._browser._state.area._id;
		var areasOptions = new Array();
		var fillAreaCombo = function(areaNode, index, areasOptions)
		{
			var option = {
					value		: areaNode._id,
					text		: areaNode._label,
					selected	: selectedArea == areaNode._id,
					children	: []
			};

			for (var i = 0; i < areaNode._areas.length; ++i)
			{
				fillAreaCombo(areaNode._areas[i], index + 1, option.children);
			}
			areasOptions.push(option);
		}

		fillAreaCombo(rootarea, 0, areasOptions);

		combo.vSelect( areasOptions);
	},
/**
 * -----------------------------------------------------------------------------
 * Met à jour la carte 2D.
 */ 
	'_updateAreaMap' : function() {
		// A cause d'un bug sous IE, les images map ne sont jamais remplacées.
		// Autant d'images map différentes que nécessaire sont créées et ajoutées
		// dans le conteneur ; l'image active est rendue visible alors que les
		// autres sont masquées.
		// La structure des conteneurs d'image map est la suivante :
		// <div id="containImageMaps">
		//   <div id="containImageMap-<type>-<area>">
		//      <map id="imageMap-<type>-<area>" name="imageMap-<type>-<area>"> ... </map>
		//      <img id="image-<type>-<area>" usemap="#imageMap-<type>-<area>" ... />
		//   </div>
		//   ...
		// </div>

		// On obtient l'élément image à rendre visible
		var state = this._browser._state;
		var mapMode = state.resultType.id == 'map' ;
		var imageMapSuffix = state.resultType.id + '-' + state.area._id;
		if (!mapMode && state.geoItem)
		{
			imageMapSuffix = state.variable.id + '-' + imageMapSuffix + '-' + state.geoItem._id;
		}

		// On rend visible uniquement l'image désirée
		var containImageMaps = this._components['CONTAIN_IMAGE_MAPS'];
		var containImageMapID = imageMapSuffix;

		// On crée l'image si elle n'existe pas encore
		var containImageMap = containImageMaps.find( '.arealayer.' + containImageMapID );
		if (containImageMap.length == 0)
		{
			var imgMapID = this._components['IMAGE_MAP_PFX'] + imageMapSuffix;
			var containImageMap = $('<div class="' + containImageMapID + ' arealayer" />');
			var ratio = [ containImageMaps.width()/1000, containImageMaps.height()/1000 ];
			if (mapMode)
			{
				var map = $('<map id="' + imgMapID + '" name="' + imgMapID + '" />');
				if (state.area._areas.length == 0)
				{
					// L'emprise actuellement sélectionnée ne contient pas d'enfants.
					// On se base donc sur les enfants du parent direct.
					var parentArea = state.area._area ? state.application.getArea(state.area._area) : null;
					if (parentArea)
					{
						var areas = parentArea._areas;
					}else
					{
						var areas = [];
					}
				}else
				{
					// L'emprise contient des enfants.
					// On se base sur les enfants directs.
					var areas = state.area._areas;
					var parentArea = state.area._area ? state.application.getArea(state.area._area) : state.area;
				}

				// Création des <area>
				// D'abord les zones d'emprises
				// On trie d'abord en fonction des z-index
				var orderedAreas = [];
				for (var i = 0; i < areas.length; ++i)
				{
					var changed = false;
					for (var j = 0; j < orderedAreas.length; ++j)
					{
						if (orderedAreas[j]._zIndex > areas[i]._zIndex)
						{
							orderedAreas.splice(j, 0, areas[i]);
							changed = true;
							break;
						}
					}
					if (!changed)
					{
						orderedAreas.push(areas[i]);
					}
				}
				// Création de la map
				for (var i = orderedAreas.length; i-- > 0; )
				{
					// On n'affecte de lien que sur les emprises non sélectionnées
					map.append($(orderedAreas[i].getAreaMap(ratio, 
							orderedAreas[i]._id == state.area._id ? -1 : orderedAreas[i]._id )));
				}
				// Ensuite le fond de carte si l'emprise sélectionnée possède un parent
				if (parentArea)
				{
					// On n'affecte de lien que sur les emprises non sélectionnées
					map.append($(parentArea.getAreaMap(containImageMaps, 
							parentArea._id == state.area._id ? -1 : parentArea._id )));
				}
				containImageMap.append(map);
			}
			else if (state.geoItem)
			{
				// On génère des <area> pour chaque élément géométrique en dehors
				// de l'élément actuellement sélectionné
				var geoMap = $('<map id="' + imgMapID + '" name="' + imgMapID + '" />');
				var geoItems = state.resultType.geoItems;
				var currentItemID = state.geoItem._id;
				for (var geoItemID in geoItems)
				{
					if (geoItems[geoItemID]._area == state.area._id)
					{
                  geoMap.append(geoItems[geoItemID].getAreaMap(
								ratio,
								currentItemID == geoItemID ? -1 : geoItemID ));
					}
				}
				containImageMap.append(geoMap);
			}
			var params = {
				'theme' : state.theme,
				'appli' : state.application._id,
				'var' : state.variable.id,
				'type' : state.resultType.id,
				'w' : containImageMaps.width(),
				'h' : containImageMaps.height(),
				'lang' : this._browser._config.locale };
			if (state.resultType.id == 'map')
			{
				if (state.area._areas.length == 0 && state.area._area != null)
				{
					params['area'] = state.area._area;
					params['select'] = state.area._id;
				}else
				{
					params['area'] = state.area._id;
				}
			}else
			{
				params['area'] = state.area._id;
				params['select'] = state.geoItem ? state.geoItem._id : '';
			}
			var url = this._browser._config.ajaxServer +
				PrevimerToolkit.getService(
					'getBackground', params);
			if (this._browser._config.ajaxProxy)
			{
				url = this._browser._config.ajaxProxy + escape(url);
			}
			containImageMap.append(
               '<img class="' + imageMapSuffix + '" src="' + url +
               '" width="' + containImageMaps.width() +
               '" height="' + containImageMaps.height() +
               '" usemap="#' + imgMapID + '" />');
			containImageMaps.append(containImageMap);
		}
      
      // Masquage de l'ancien élément, et affichage du nouveau
      containImageMaps.find('.arealayer').hide();
		containImageMaps.find('.arealayer.' + containImageMapID).show();

		// Mise à jour de l'état du bouton de retour à l'emprise parente
		if (state.area._area)
		{
			this._components['BUTTON_UP_AREA'].find('a').addClass('enabled');
			this._components['BUTTON_UP_AREA'].find('a').removeClass('disabled');
		} else {
			this._components['BUTTON_UP_AREA'].find('a').addClass('disabled');
			this._components['BUTTON_UP_AREA'].find('a').removeClass('enabled');
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix de type de résultat.
 */ 
	'_loadResultTypes' : function()
	{
		// Mise à jour des types de résultat disponibles
		var currentState = this._browser._state;
		var resultTypes =
			currentState.application.getVariables()[currentState.variable.id].resultTypes;
		var i = 0;
		var selectIdx = 0;
		this._components['CONTAIN_RTYPE_BUTTONS'].find('a').removeClass( 'selected' );
		for (var j = 0; j < PrevimerIHMManager.VALID_RESULT_TYPES.length; ++j)
		{
			var type = PrevimerIHMManager.VALID_RESULT_TYPES[j][0];
			var button = this._components['CONTAIN_RTYPE_BUTTONS'].find('.' +  type);
			if (resultTypes[type])
			{
				if (this._browser._state.resultType.id == type)
				{
					button.find('a').addClass( 'selected' );
				}
				button.find('a').get(0).resultTypeID = type;
				button.find('a').addClass( 'enabled');
				button.find('a').removeClass( 'disabled' );
			}else
			{
				button.find('a').get(0).resultTypeID = null
				button.find('a').addClass( 'disabled' );
				button.find('a').removeClass( 'enabled');
			}
			button.find('a').click(function(){
				if ( this.resultTypeID &&
					!$(this).hasClass( 'disabled' ) &&
					!$(this).hasClass( 'selected' ) )
				{
					$(window.browser).get(0).getCBManager().onSelectResultType(
							this.resultTypeID );
				}
				return false;
			});
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix d'un point géographique.
 */
	'_loadGeoitemCombo' : function() {
		var combo = this._components['COMBO_GEOITEMS'];
		var state = this._browser._state;
		var geoOptions = [];
		if (state.resultType.id != 'map')
		{
			var geoItems = state.resultType.geoItems;
			for (var geoItemID in geoItems)
			{
				if (geoItems[geoItemID]._area == state.area._id )
				{
					geoOptions.push({
						value		: geoItemID,
						text		: HtmlDecode( 
								geoItems[geoItemID]._label.replace( 
										new RegExp( " [\(][^\(]*[\)]$" ),
										'' 
								)
						),
						selected	: geoItems[geoItemID]._id == state.geoItem._id 
					});
				}
			}
		}
		combo.vSelect( geoOptions, {disabled: geoOptions.length <= 0});
		if (geoOptions.length > 0)
		{
			$.vSelect.syncDropDown(this._components['COMBO_GEOITEMS']);
			this._components['COMBO_GEOITEMS'].show();
		}else
		{
			this._components['COMBO_GEOITEMS'].hide();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix de profondeur.
 */ 
	'_loadDepthCombo' : function()
	{
		if (!this._browser._state.resultType.hasDepths) {
			this._components['COMBO_DEPTH'].parent().css('display', 'none');
		}
		else
		{
			var combo = this._components['COMBO_DEPTH'];
			var depthOptions = new Array();
			for (depth in this._browser._state.resultType.depths)
			{
				depthOptions.push({
					value: depth,
					text: this._browser._state.resultType.depths[depth],
					selected:this._browser._state.depth == depth
					});
			}
			combo.vSelect( depthOptions );
			this._components['COMBO_DEPTH'].parent().css('display', 'block');
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix d'heure.
 */ 
	'_loadHoursCombo' : function(fromAnim)
	{
		var timeConfig = this._browser._state.resultType.getTimeConfig();
		var combo = this._components['COMBO_HOURS'];
		if (timeConfig.unit == 'hour' || timeConfig.unit == 'minute')
		{
			this._components['BUTTON_PREV_DAY'].css('display', 'block');
			this._components['BUTTON_NEXT_DAY'].css('display', 'block');
			var hours = timeConfig.getValidHours(this._components['CALENDAR'].date);
			var offset = this._browser._state.hourOffset;
			hourElements = [];
			for (var i = 0; i < hours.length; ++i)
			{
				hourElements.push({
						value:hours[i][1],
						text:hours[i][0],
						selected:hours[i][1] == offset
				});
			}
			combo.vSelect( hourElements, {disabled:false} );
		}else
		{
//			this._components['HOUR_GROUP_BUTTON'].toggleClass( 'buttons_group' );
			this._components['BUTTON_PREV_DAY'].css('display', 'none');
			this._components['BUTTON_NEXT_DAY'].css('display', 'none');
			hourElements = [{
				value : '--h',
				text : '--h',
				selected : true
			}];
			combo.vSelect(hourElements, {disabled:true});
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit la liste déroulante de choix de formats.
 */ 
	'_loadFormatCombo' : function() {
		var combo = this._components['COMBO_FORMATS'];
		var formats = this._browser._state.resultType.formats;
		var formatOptions = new Array();
		if (formats.length == 0 || !this._browser._animManager.isRunning() )
		{
			formatOptions.push({
				value		: 'image',
				text		: 'Image',
				selected	: true
			});
		}
		else
		{
			for (var i = 0; i < formats.length; ++i)
			{

				formatOptions.push({
					value	: formats[i][0],
					text	: formats[i][1],
					selected	: formats[i][0] == 'image'
				});
			}
		}
		combo.vSelect( formatOptions, {disabled:formatOptions <= 0} );
	},
/**
 * -----------------------------------------------------------------------------
 * Remplit le panneau contenant les liens d'animations.
 */ 
	'_loadAnimationPane' : function() {
		if (this._browser._state.animation == null)
		{
			// Pas d'animation sélectionnée
			var anim = this._browser._state.resultType.getAnimations();
			var html = '';
			
			for (var i = 0; i < anim.length; ++i) {
				var animButtons = this._components['CONTAIN_ANIM_LINKS'].find('.button_group > .duration > a');
				$(animButtons[i]).find('span').html( anim[i].getLabel(this._browser._config.locale) );
				animButtons[i].animIDX = i;
				$(animButtons[i]).unbind('click');
				$(animButtons[i]).click(function(){
					if ( !$(this).hasClass('selected') && !$(this).hasClass('disabled'))
					{
						$(this).addClass( 'selected');
						$(window.browser).get(0).getCBManager().onAnimStart(this.animIDX);
					}
					return false;
				});
			}
			this._components['CONTAIN_ANIM_BUTS'].find('.button > a').addClass('disabled');
		}
		else
		{
			// Affiche le panneau relatif à l'animation en cours
			this._components['CONTAIN_ANIM_LINKS'].find('.button > a').addClass('disabled');
			this._components['CONTAIN_ANIM_BUTS'].find('.button > a').removeClass('disabled');
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Met à jour les liens permanents de la page.
 */ 
	'_loadPermalinks' : function()
	{
		// Permalinks
		var rawDatedPermaLink = this._browser._state.getPermaLink(true);
		var datedPermaLink = this._browser._config.appURL + rawDatedPermaLink;
		this._components['LINKS_PERMALINK'].attr( 'href', this._browser._config.appURL +
				this._browser._state.getPermaLink(false) + '/(type)/' + 
				this._browser._config.displaymode +
				this._browser._config.URLSuffix );
		this._components['LINKS_PERMALINK'].removeClass('disabled');
		this._components['LINKS_PERMALINK_T'].attr('href', datedPermaLink +
				'/(type)/' + this._browser._config.displaymode +
				this._browser._config.URLSuffix );
		this._components['LINKS_PERMALINK_T'].removeClass('disabled');

		// Lien d'impression dans le menu d'accès rapide
		if (this._components['LINKS_PRINT'])
		{
			that = this;
			this._components['LINKS_PRINT'].unbind('click');
			this._components['LINKS_PRINT'].click(function()
			{
				if( !$(this).hasClass('disabled'))
				{
					var printHref = '';
					if (that._browser._config.locale == 'en')
					{
						printHref += '/en/layout/set/print' + datedPermaLink.substring(3);
					}else
					{
						printHref += '/layout/set/print' + datedPermaLink + '/(type)/image';
					}
					window.open(printHref, "", "resizable=yes,directories=no,location=no,menubar=yes,status=no,toolbar=no,scrollbars=yes");
				}
				return false;
			});
			this._components['LINKS_PRINT'].removeClass('disabled');
		}

		// Liens de changement de langue dans le menu d'accès rapide
		if (this._components['LINKS_EN'])
		{
			this._components['LINKS_EN'].attr( 'href', '/en' + datedPermaLink + this._browser._config.URLSuffix );
		}
		if (this._components['LINKS_FR'])
		{
			this._components['LINKS_FR'].attr( 'href', datedPermaLink.substring(3) + this._browser._config.URLSuffix );
		}

		// Liens de changement de mode d'affichage
		var pos = this._browser._config.URLSuffix.indexOf('#');
		if (this._components['LINKS_IMAGEMODE'])
		{
			this._components['LINKS_IMAGEMODE'].attr( 'href', datedPermaLink +
					'/(type)/image' +
					( pos == -1 ? '' : this._browser._config.URLSuffix.substr( pos ) ) );
		}
		if (this._components['LINKS_FLASHMODE'])
		{
			this._components['LINKS_FLASHMODE'].attr( 'href', datedPermaLink +
					( pos == -1 ? '' : this._browser._config.URLSuffix.substr( pos ) ) );
		}
		
		// Liens d'accès rapide aux thématiques
		if (this._rapidThemeUrl && (this._rapidThemeUrl.length >= 1))
		{
			for (var i = 0; i < this._rapidThemeUrl.length ; ++i)
			{
				this._rapidThemeUrl[i].node.href = this._rapidThemeUrl[i].baseUrl +
					rawDatedPermaLink + this._browser._config.URLSuffix ;
			}
		}

		// Redirection de l'utilisateur après authentification
		if ( this._components['USER_REDIRECT_URI'] )
		{
			if (this._browser._config.locale == 'en' )
			{
				this._components['USER_REDIRECT_URI'].attr( 'value', datedPermaLink.substring(3) + this._browser._config.URLSuffix );
			}else 
			{
				this._components['USER_REDIRECT_URI'].attr( 'value', datedPermaLink + this._browser._config.URLSuffix );
			}
		}
/*
		// Liens d'abonnement à l'image actuelle
		if ( this._components['LINK_SUBSCRIPTION'] )
		{
		   var theme = this._browser._state.theme;
		   var application = this._browser._state.application._id;
		   this._components['LINK_SUBSCRIPTION'].attr( 
		         'href', '/picturesubscription/edit/0/(theme)/' +
		         theme +
		         '/(application)/' +
		         application +
		         this._browser._state.getPermaLink(false) );
		}
*/
	},

	/**
	 * Appelle une commande Flash.
	 * @param command la commande Flash à appeler
	 * @param params une structure contenant les paramètres à transmettre
	*/
	'_callFlashCommand' : function(command, params)
	{
		if (!this._flashInit)
		{
			if (this._ieTimeoutParams == null) 
			{
				this._ieTimeoutParams = [ command, params ];
			}
			if ( --this._flashTest > 0 )
				window.setTimeout("PrevimerToolkit.getGlobalInstance('" + this._objectID + "')._callFlashCommand()", 50);
			else {
				window.location = this._browser._config.appURL + this._browser._state.getPermaLink(true) + '/(type)/image' + this._browser._config.URLSuffix;
			}
		}else
		{
			if (this._ieTimeoutParams != null)
			{
				command = this._ieTimeoutParams[0];
				params = this._ieTimeoutParams[1];
				this._ieTimeoutParams = null;
			}
			this._updateFlash();
			if (command != 'changeAppParams')
			{
				$('#fla_object_colormap').get(0).command(command, params);
			}
		}
	},

	/**
	 * Met à jour le Flash lors d'un changement de variable, type, carte, ...
	 */
	'_updateFlash' : function()
	{
		var params = this._browser._state.getState();
		params['lang'] = this._browser._config.locale;
		params['date'] = this._browser._state.date.getTime();
		if (this._flashPreviousParams == null
				|| this._flashPreviousParams.theme != params.theme
				|| this._flashPreviousParams.appli != params.appli
				|| this._flashPreviousParams.variable != params.variable
				|| this._flashPreviousParams.type != params.type
				|| this._flashPreviousParams.area != params.area
				|| this._flashPreviousParams.level != params.level
				|| this._flashPreviousParams.geoid != params.geoid
				|| this._flashPreviousParams.lang != params.lang)
		{
			this._flashPreviousParams = params;
			$('#fla_object_colormap').get(0).command('changeAppParams', {
				appParams : params,
				anim : this._browser._state.resultType.getAnimationsSettings()
			});
		}
	},

	/**
	 * Gestionnaire d'événements en provenance de l'applet Flash de visualisation.
	 */
	'onFlashEvent' : function(state)
	{
		this._browser._state.setFlashState( state );
		if (state.command == 'load')
		{
			if (state.status == 'complete')
			{
				this._flashInit = true;
				$('#fla_object_colormap').command('OK');
			}
		}else if (state.command == 'getResult')
		{
			if (state.status == 'complete')
			{
				this._imageLoading = false;
				if (this._imageWaiting != null)
				{
					var params = this._imageWaiting;
					this._imageWaiting = null;
					this._loadResultImage(params);
				}
				this._components['PROGRESS_LABEL'].html( '');
				this._components['PROGRESS_BAR'].css( 'width', '0%' );
			}
		}else if (state.command == 'anim')
		{
			if (state.status == 'start'
				|| state.status == 'progress')
			{
				this.onAnimLoading(
						[ state.progress.imagesLoaded,
						  state.progress.imagesTotal ]);
			}else if (state.status == 'complete')
			{
				this.onAnimLoaded();
				if ( state.anim.isLoop && !this._components['BUTTON_ANIM_LOOP'].hasClass( 'selected' ) )
				{
					this._components['BUTTON_ANIM_LOOP'].addClass( 'selected' );
				}
			}else if (state.status == 'play'
				|| state.status == 'pause')
			{
				this._browser._animManager.setRunningMode(state.status);
				this.onNextFlashImage(
						[ state.anim.index - 1, state.anim.count, state.anim.date ]);
				if ( state.anim.isLoop && !this._components['BUTTON_ANIM_LOOP'].hasClass( 'selected' ) )
				{
					this._components['BUTTON_ANIM_LOOP'].addClass( 'selected' );
				}
			}else if (state.status == 'stopped')
			{
				this._browser._animManager.setRunningMode(state.status);
				this.onAnimStopped();
			}
		}
	}
}

/**
 * ============================================================================= 
 * AnimationSettings
 * Paramètres d'une animation 
 * ============================================================================= 
 */ 
AnimationSettings = function(domNode, defaultStep)
{
	this.unit = $(domNode).attr('unit');
	this.length = parseInt( $(domNode).attr('length') );
	this.step = parseInt($(domNode).attr('step'));
	if (!this.step)
	{
		this.step = defaultStep;
	}
}

AnimationSettings.prototype = {
/**
 * -----------------------------------------------------------------------------
 * Obtient les paramètres de l'animation.
 */
	'getSettings' : function()
	{
		return {
			unit : this.unit,
			length : this.length,
			step : this.step
		};
	},
/**
 * -----------------------------------------------------------------------------
 * Obtient la représentation codifiée de ce paramètre d'animation.
 */
	'getLabel' : function(locale) {
		if (this.unit == 'minute' && (this.length % 60 == 0)) {
			var label = (this.length / 60) + 'h';
		}
		else if (this.unit == 'hour' && (this.length % 24 == 0)) {
			var label = (this.length / 24) + (locale == 'fr' ? 'j' : 'd');
		}
		else {
			var label = this.length;
			switch (this.unit) {
				case 'minute' : label += 'm'; break;
				case 'day' :    label += (locale == 'fr' ? 'j' : 'd'); break;
				case 'hour' :   label += 'h'; break;
				case 'month' :  label += 'mo'; break;
				case 'year' :   label += (locale == 'fr' ? 'a' : 'y'); break;
			}
		}
		return label;
	},
/**
 * -----------------------------------------------------------------------------
 * Obtient la liste des URLs composant l'animation.
 * @return deux tableaux, un contenant les URLs, le deuxième les timestamp en ms
 */
	'getURLs' : function(state, config)
	{
		// On récupère les paramètres
		var params = state.getRequestParams(CurrentState.TYPE_RESULT, config);
		
		// On va faire varier la date, à partir du moment de référence stocké
		// initialement dans les paramètres, en fonction des paramètres de cet
		// objet
		var limits = this.getLimits( params.date, state, true );
		var date = new Date(limits.startDate);
		// Calcul des URLs
		var URLs = [];
		var timestamps = [];
		params.nf404 = "true";
		while (date.getTime() < limits.endDate)
		{
			// Stockage de l'URL
			params.date = date.getTime();
			URLs.push(PrevimerToolkit.getResultRequest(params, config));
			timestamps.push(params.date);
			
			// Pas de temps suivant
			switch (this.unit)
			{
				case 'minute' :
					date.setUTCMinutes(date.getUTCMinutes() + this.step);
					break;
				case 'hour' :
					date.setUTCHours(date.getUTCHours() + this.step);
					break;
				case 'day' :
					date.setUTCDate(date.getUTCDate() + this.step);
					break;
				case 'week' :
					date.setUTCDate(date.getUTCDate() + 7 * this.step);
					break;
				case 'month' :
					date.setUTCMonth(date.getUTCMonth() + this.step);
					break;
				case 'year' :
					date.setUTCFullYear(date.getUTCFullYear() + this.step);
					break;
			}
		}
		return [ URLs, timestamps ];
	},
	
	'getLimits' : function( dateStart, state, inclusive )
	{
		var startDate = dateStart;

		// On calcule la date de fin
		switch (this.unit)
		{
			case 'minute' :
				var endDate = startDate + (inclusive ? this.length : this.length - 1) * 60000;
				break;
			case 'hour' :
				var endDate = startDate + (inclusive ? this.length : this.length - 1) * 3600000;
				break;
			case 'day' :
				var endDate = startDate + (inclusive ? this.length : this.length - 1) * 24 * 3600000;
				break;
			case 'week' :
				var endDate = startDate + (inclusive ? this.length : this.length - 1) * 7 * 24 * 3600000;
				break;
			case 'month' :
				params.date.setUTCMonth(params.date.getUTCMonth() + (inclusive ? this.length : this.length - 1));
				var endDate = params.date.getTime();
				break;
			case 'year' :
				params.date.setUTCFullYear(params.date.getUTCFullYear() +(inclusive ? this.length : this.length - 1));
				var endDate = params.date.getTime();
				break;
		}
		if (endDate > state.resultType.getTimeConfig().to)
		{
			endDate = state.resultType.getTimeConfig().to;
		}
		return {
			startDate:startDate,
			endDate:endDate
		};
	}
}

/**
 * ============================================================================= 
 * Animation
 * Gestionnaire d'animations
 * ============================================================================= 
 */ 
Animation = function()
{
	// Index de la vitesse de l'animation dans le tableau ANIM_SPEED
	this._animationSpeed = 2;
	// Drapeau indiquant si l'animation est en cours d'exécution ou non
	this._runningMode = Animation.MODE_STOP;
	// Index de l'image actuellement affichée au sein de l'animation
	this._imageIndex = 0;
	// Images composant l'animation
	this._images = [];
	// ID du timeout en cours
	this._timeoutID = null;
	this._loadTimeoutID = null;
	// Listeners
	this._listeners = [ ];
	this._stopListeners = [ ];
	this._onloadallCB = null;
	this._onloadingallCB = null;
	// Rebouclage
	this._loop = (PrevimerToolkit.getCookie("PVMRAnimLoop", "true") == "true");

	// Gestion de l'accès global à l'objet, utilisé par le setTimeout
	// Après appel de la méthode, l'id de l'objet est disponible dans l'attribut
	// _objectID
	this.className = 'Animation';
	PrevimerToolkit.registerGlobalInstance(this);
}
// Vitesses d'animation disponibles (en ms)
Animation.ANIM_SPEED = [ 1000, 750, 500, 250, 100 ];
// Mode d'exécution : arrêté, en cours, gelé
Animation.MODE_STOP = 0;
Animation.MODE_RUN = 1;
Animation.MODE_PAUSE = 2;
Animation.MODE_LOADING = 3;
Animation.prototype = {
/**
 * -----------------------------------------------------------------------------
 * Charge une animation.
 * @param params paramètres de l'animation (un objet AnimationSettings)
 */ 
	'load' : function(URLs, onloadCB, onloadingallCB, onloadallCB) {
		this._images = [];
		this._imagesStatus = [];
		this._onloadallCB = onloadallCB;
		this._onloadingallCB = onloadingallCB;
		var that = this;
		for (var i = 0; i < URLs.length; ++i)
		{
			var image = new Image();
			image.indexForError = i;
			image.ready = false;
			image.onabort = function()
			{
				that._images[this.indexForError] = null;
				this.ready = true;
			};
			image.onerror = function()
			{
				that._images[this.indexForError] = null;
				this.ready = true;
			};
			image.onload = function()
			{
				this.ready = true;
				this.onloadRequested();
			};
			image.onloadRequested = onloadCB;
			image.src = URLs[i];
			this._images.push(image);
		}
		this._loadTimeoutID = window.setTimeout(
				"PrevimerToolkit.getGlobalInstance('" + this._objectID + "')._checkLoaded()",
				200);
	},
	'_checkLoaded' : function()
	{
		if (this._loadTimeoutID != null)
		{
			var loadedCount = 0;
			for (i = this._images.length; i-- > 0; )
			{
				if (this._images[i] == null || this._images[i].ready)
				{
					++loadedCount;
				}
			}
			if (loadedCount < this._images.length)
			{
				if (this._onloadingallCB != null)
				{
					PrevimerToolkit.callMethod(this._onloadingallCB, [ loadedCount,  this._images.length ]);
				}
				this._loadTimeoutID = window.setTimeout(
						"PrevimerToolkit.getGlobalInstance('" + this._objectID + "')._checkLoaded()",
						200);
				return;
			}
			for (i = this._images.length; i-- > 0; )
			{
				if ( this._images[i] == null )
					this._images.splice(i, 1);
			}
			this._loadTimeoutID = null;
			PrevimerToolkit.callMethod(this._onloadallCB, [ this ]);
		}
	},
	'toggleLoop' : function() {
		this._loop = !this._loop;
		PrevimerToolkit.setCookie("PVMRAnimLoop", this._loop ? 'true' : 'false');
	},
/**
 * -----------------------------------------------------------------------------
 * Référence une fonction callback à appeler lors du changement d'image.
 * @param listener une fonction callback à appeler
 */ 
	'addListener' : function(listener)
	{
		this._listeners.push(listener);
	},
/**
 * -----------------------------------------------------------------------------
 * Référence une fonction callback à appeler lors de l'arrêt des animations.
 * @param listener une fonction callback à appeler
 */ 
	'addStopListener' : function(listener) {
		this._stopListeners.push(listener);
	},
/**
 * -----------------------------------------------------------------------------
 * Dispatche un événement de changement d'images à l'ensemble des listeners
 * référencés.
 */  
	'_fireImageChangedEvent' : function() {
		for (var i = 0; i < this._listeners.length; ++i) {
			PrevimerToolkit.callMethod(this._listeners[i],
				[ this._images[this._imageIndex], this._imageIndex, this ]);
		}
		if (this._runningMode == Animation.MODE_RUN) {
			this._timeoutID = window.setTimeout(
				"PrevimerToolkit.getGlobalInstance('" + this._objectID + "').nextStep()",
				Animation.ANIM_SPEED[this._animationSpeed]);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Dispatche un événement d'arrêt d'animation à l'ensemble des listeners
 * référencés.
 */  
	'_fireStoppedEvent' : function() {
		for (var i = 0; i < this._stopListeners.length; ++i) {
			PrevimerToolkit.callMethod(this._stopListeners[i], this);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Change l'état d'une animation ( play, pause, stopped )
 * Utiliser pour la gestion des animations utilisant l'applet FLASH
 */
	'setRunningMode' : function(mode)
	{
		var animMode = '';
		if (mode == 'play')
			animMode = Animation.MODE_RUN;
		else if (mode == 'pause')
			animMode = Animation.MODE_PAUSE;
		else if (mode == 'stopped')
			animMode = Animation.MODE_STOP;
      else if (mode == 'loading')
         animMode = Animation.MODE_LOADING;
		this._runningMode = animMode;
	},
/**
 * -----------------------------------------------------------------------------
 * Démarre l'animation.
 * Si l'animation était déjà en cours d'exécution, cette méthode n'a aucun effet.
 * Si elle était en pause, cette méthode repasse en mode play.
 */ 
	'play' : function()
	{
		if (this._runningMode != Animation.MODE_RUN)
		{
			this._runningMode = Animation.MODE_RUN;
			this._fireImageChangedEvent();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Arrête l'animation.
 * Si l'animation était déjà arrêtée, cette méthode n'a aucun effet.
 */  
	'stop' : function() {
		if (this._runningMode != Animation.MODE_STOP) {
			if (this._runningMode == Animation.MODE_RUN
					&& this._timeoutID) {
				window.clearTimeout(this._timeoutID);
				this._timeoutID = null;
			}
			this._runningMode = Animation.MODE_STOP;
			this._imageIndex = 0;
			this._fireStoppedEvent();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Gèle l'animation.
 * Si l'animation était arrêtée, cette méthode la place en cours d'exécution, et
 * la gèle à la première image.
 * Si l'animation était déjà en mode pause, cette méthode n'a aucun effet. 
 */   
	'pause' : function() {
		if (this._runningMode != Animation.MODE_PAUSE) {
			if (this._runningMode == Animation.MODE_RUN
					&& this._timeoutID) {
				window.clearTimeout(this._timeoutID);
				this._timeoutID = null;
			}
			this._runningMode = Animation.MODE_PAUSE;
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Indique si une animation est en cours ou pas (i.e. si elle est en pause ou en
 * cours, ou arrêtée). 
 */   
	'isRunning' : function() {
		return this._runningMode != Animation.MODE_STOP;
	},
/**
 * -----------------------------------------------------------------------------
 * Indique si une animation est en pause ou pas (i.e. si elle est en pause,
 * ou en cours ou arrêtée). 
 */   
	'isPaused' : function() {
		return this._runningMode == Animation.MODE_PAUSE;
	},
/**
 * -----------------------------------------------------------------------------
 * Augmente la vitesse d'exécution de l'animation.
 */ 
	'setFaster' : function() {
		if (this._animationSpeed < Animation.ANIM_SPEED.length - 1) {
			++this._animationSpeed;
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Diminue la vitesse d'exécution de l'animation.
 */ 
	'setSlower' : function() {
		if (this._animationSpeed > 0) {
			--this._animationSpeed;
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Demande l'affichage de l'image suivante de l'animation.
 * Cette méthode est appelée automatiquement en mode RUN, ou manuellement
 * en mode PAUSE.  
 */ 
	'nextStep' : function() {
		this._timeoutID = null;
		var paused = this._runningMode == Animation.MODE_PAUSE;
		var lastValidIdx = this._imageIndex;
		do {
			++this._imageIndex;
			if (this._imageIndex >= this._images.length) {
				if (paused) {
					if (this._loop) {
						this._imageIndex = 0;
					}
					else {
						this._imageIndex = lastValidIdx;
						return; // On reste sur le dernier index valide
					}
				}
				else {
					this._imageIndex = 0;
					if (!this._loop) {
						this._runningMode = Animation.MODE_STOP;
					}
				}
			}
			if (this._imageIndex == lastValidIdx && this._runningMode != Animation.MODE_STOP) {
				// On a rebouclé, on reste sur le dernier index valide
				return;
			}
		}
		while (this._images[this._imageIndex] == null
				&& this._runningMode != Animation.MODE_STOP);
		this._fireImageChangedEvent();
		if (this._runningMode == Animation.MODE_STOP) {
			this._fireStoppedEvent();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Demande l'affichage de l'image précédente de l'animation.
 * Cette méthode est appelée manuellement en mode PAUSE.  
 */ 
	'prevStep' : function() {
		var lastValidIdx = this._imageIndex;
		do {
			--this._imageIndex;
			if (this._imageIndex < 0) {
				if (this._loop) {
					this._imageIndex = this._images.length - 1;
				}
				else {
					this._imageIndex = lastValidIdx;
					return; // On reste sur le dernier index valide
				}
			}
			if (this._imageIndex == lastValidIdx) {
				// On a rebouclé, on reste sur le dernier index valide
				return;
			}
		}
		while (this._images[this._imageIndex] == null);
		this._fireImageChangedEvent();
	}
}


/**
 * ============================================================================= 
 * PrevimerBrowser
 * Objet principal de l'afficheur d'images Previmer 
 * ============================================================================= 
 */ 
PrevimerBrowser = function(config, state, helpTopics) {
	// Configuration
	this._config = config;
	// Paramètres métier
	this._state = new CurrentState(state);
	// Topics d'aide
	this._helpTopics = helpTopics;
	this._helpTopics._config = config;
	// Gestionnaire d'IHM
	this._ihmManager = new PrevimerIHMManager(this);
	// Gestionnaire d'animations
	this._animManager = new Animation();
	this._animManager.addListener(
		'PrevimerToolkit.getGlobalInstance("' + this._ihmManager._objectID + '").onNextImage');
	this._animManager.addStopListener(
		'PrevimerToolkit.getGlobalInstance("' + this._ihmManager._objectID + '").onAnimStopped');
	// Informations de version
	this._version = {
		'appID'    : 'previmer-jsbrowser',
		'appLabel' : 'Previmer results browser',
		'version'  : '3.0',
		'build'    : '20090709'
	};

	// On déclare l'objet dans les instances globales
	this.className = 'PrevimerBrowser';
	PrevimerToolkit.registerGlobalInstance(this);
	PrevimerToolkit.locale = config.locale;
	
	// Préparation des paramètres Ajax (serveur et proxy)
	this._config.ajaxServer ='';
	if (this._config.protocol && this._config.protocol != '' )
	{
		this._config.ajaxServer += this._config.protocol + '://';
	}
	if (this._config.server && this._config.protocol != '' )
	{
		this._config.ajaxServer += this._config.server;
	}
	if (this._config.port && this_config.port != '' )
	{
		this._config.ajaxServer += ':' + this._config.port;
	}
	this._config.ajaxServer += '/';
	if (this._config.URLPrefix) {
		this._config.ajaxServer += this._config.URLPrefix;
	}
	// LD Ajout du node ID pour le test d'accèssibilité de la requête
	if (this._config.nodeID) {
	   this._config.ajaxServer += 'nodeid/' + this._config.nodeID + '/' ;
	}
	//////////////////////////////////////////////////////////////////
	this._config.ajaxProxy = this._config.proxy ? this._config.proxy : null;
	var ajaxEngine = new VAjaxEngine(this._config,
		'PrevimerToolkit.getGlobalInstance("' + this._objectID +
		'").onApplicationLoaded');
	ajaxEngine.doGet(PrevimerToolkit.getService(
		'getApplicationInfos',
		this._state.getRequestParams(CurrentState.TYPE_APP_INFOS, this._config)));
}
PrevimerBrowser.prototype = {
/**
 * -----------------------------------------------------------------------------
 * Callback appelée sur réception de la trame de description d'une application
 * en provenance de Tomcat.
 * @param context l'objet d'accès aux informations Ajax
 */ 
	'onApplicationLoaded' : function(context) {
		this._state.initObjects(new Application(context.lastDocument)); 
		this._ihmManager.initIHM();
	},
	'getCBManager' : function() {
		return this._ihmManager;
	}
}
