/*
 * WFG-LIGHTSTREAMER
 * Web Client - wfgLightstreamer.js - Version 1.009
 * Copyright (c) 2009 Web Financial Group S.A. All Rights Reserved.
 * 
 * Requirements:
 * - mootools-1.2.2-ls.js
 * 
 */
	
// Indica el estado global de Lightstreamer (ON, OFF)
var _lsStatusGlobal= 'ON';

// Definicion de campos por defecto
var _numberField = {dec:2, dec_sep:',', ths_sep:'.', css_set:'css'};

// Definir objeto para indicar si hay filas ocultas en las tablas
var _hiddenRows = new Object();

// Inicializar variable de configuración de tablas
var _confTables = null;

//////////////// Tables Management //////////////////
	
/**************************************************************
 Description:
	Define una nueva tabla del tipo recibido
	
 Param:
 	- tableName: nombre de la tabla lógica
 	- tableType: tipo de la tabla lógica
 	- numGroup: nº del grupo de datos  (solo se utiliza si los grupos se definen manualmente)
 	
 Return:
 	Tabla generada
 	
 NOTA: Si se tiene un listado paginado:
	var _confTables = {
		ls_table_listado:{
			group1:[item, item, item, ...],
			group2:[item, item, item, ...],	
			group3:[item, item, item, ...],				
			schema: null		// array o null	
		}
	} 	
**************************************************************/
var newLsTable = function(tableName, tableType, numGroup){
	var _itemGroup = null;
	var _itemSchema = null;
	
	if ((_confTables) && (_confTables != null)){	// Grupos definidos manualmente 
		if ( _confTables[tableName]){
			var _group = 'group' + numGroup;
			_itemGroup = _confTables[tableName][_group];
			_itemSchema = _confTables[tableName]['schema'];	
		}
	}

	switch(tableType){
		case 'overwrite':
			// Preparar la tabla por si hay paginación			
			if (_itemGroup != null){
				var _longGroup = _itemGroup.length;
				var _dif = _lsDataConf[tableName]['pagination'] - _longGroup;
				rebuildLsTable(tableName, _dif)
			}				
			
			// Crear tabla
			var _newTable = new OverwriteTable(_itemGroup, _itemSchema, 'MERGE');
			_newTable.setDataAdapter(_lsDataConf[tableName]['data_adapter']);
			_newTable.setSnapshotRequired(true);
			_newTable.setRequestedMaxFrequency(_lsDataConf[tableName]['frequency']);
			_newTable.onItemUpdate = updateItem;
			_newTable.onChangingValues = formatValues;
			_newTable.setPushedHtmlEnabled(true);		
			break;
		default:
			var _newTable = null;
	}
	
	return _newTable;
}

/**************************************************************
 Description:
	Oculta/muestra filas de la tabla dependiendo de la página que esté visible. Se utiliza acuando la última página tiene menos filas que el resto
	- Si dif = 0: pag. interna (mostrar todas las filas)
	- Si pag > 0: ult. pag. Eliminar filas sobrantes
	
 Param:
 	- table: tabla 
 	- dif: nº de filas que sobran
**************************************************************/
var rebuildLsTable = function(table, dif){

	if (dif == 0){	// Página interna
		if (_hiddenRows[table]){	// hay filas ocultas --> mostrarlas
			var _aux = '#' + table + ' .ls_hidden';
			$$(_aux).each(function(_elem){
				_elem.removeClass('ls_hidden');
			});
			_hiddenRows[table] = false;
		}
	}
	else{	// Última página
		if (_hiddenRows[table] == false){
			// ocultar filas visibles sobrantes (las 'dif' ultimas)
			var _aux = '#' + table + ' .ls_row';
			var _auxElem = $$(_aux).reverse();
			for (x=dif-1; x>=0; x--){			
//			for (x=0; x<dif; x++){
				_auxElem[x].addClass('ls_hidden');
			}								
			_hiddenRows[table] = true;
		}		
	}
}

/**************************************************************
 Description:
	Cambia los elementos de una tabla. Se utiliza en las paginaciones. esta función es llamada desde el frontal cuando se quiera cambiar de página
	
 Param:
 	- idTable: identidficador de la tabla en la que se va a hacer el cambio 
	 	(ej: ls_table_xxx => idTable=xxx)
 	- numGroup: nº del grupo de elementos que va a estar activo => página
**************************************************************/
var changeLsTable = function(idTable, numGroup){
	var _t = 'ls_table_' + idTable;
	
	var _table = newLsTable(_t, _lsDataConf[_t]['table_type'], numGroup);	
	if (_table != null) pushPage.addTable(_table, _t);	
}

//////////////// Master Push-Page Configuration //////////////////

/***************************************************************
 Description:		
	Actualiza el status
	
Param:
	- newStatus: texto descriptivo del status

***************************************************************/
var showLsStatus = function(newStatus) {
	var _statusContainer = document.getElementById('ls_status');
	if (_statusContainer) {
		_statusContainer.innerHTML = newStatus;
	}
}
	
/**************************************************************
 Description:
	Inicializa el motor y genera las tablas de datos
 
 Param:
 	- domain: dominio del servidor actual	
*************************************************************/
var initLsEngine = function(domain){
	if ((_lsStatusGlobal == 'ON') && (_lsPushConf['ls_status'] == 'ON')){
	
		// Inicializar el motor	
		var _push = readCookie('lsPushDomain');
		if (!_push) _push = "push";
		var _lsHost = _push + '.' + domain;
			
		pushPage = new PushPage();
		pushPage.onClientAlert = null;
		pushPage.context.setDebugAlertsOnClientError(false); 
		pushPage.context.setRemoteAlertsOnClientError(true);		
		pushPage.context.setDomain(domain);
		
		pushPage.onEngineCreation = function(lsEngine) {
			lsEngine.onClientAlert = null;
			lsEngine.context.setDebugAlertsOnClientError(false);
			lsEngine.context.setRemoteAlertsOnClientError(true);
			lsEngine.connection.setLSHost(_lsHost); 
			lsEngine.connection.setLSPort(80); 
			lsEngine.connection.setAdapterName(_lsPushConf['adapter']);
			lsEngine.changeStatus('STREAMING');			
		};
	
		// Status notification
		pushPage.onEngineReady = function(lsEngine) {
			lsEngine.onClientAlert = null;
			
			lsEngine.onServerError = function(errorCode, errorMessage){
				if (errorCode == 1) {	// usuario incorrectos
					if (window.showErrorMsg) showErrorMsg();
					else return false;										
				}
			};
								
			lsEngine.onStatusChange = function(newStatus) {
				showLsStatus(newStatus);
			};
			lsEngine.onStatusChange(lsEngine.getStatus());
		};
		
		pushPage.onEngineLost = function() {
			showLsStatus('WAITING FOR ENGINE...');
		};
	
		pushPage.bind();
		pushPage.createEngine('WFGCommonEngine', '/js_comun/lightstreamer/ls/', 'SHARE_SESSION', true);
				  	  
		// Definir tablas	
		for (var _t in _lsDataConf){					
			if ($(_t)){
				// Inicializar elementos de tipo 'number'
				for( var _el in _lsDataConf[_t]['fields'] ){			
					if (_lsDataConf[_t]['fields'][_el]['type'] == 'number') _lsDataConf[_t]['fields'][_el] = $merge(_numberField, _lsDataConf[_t]['fields'][_el]);		
				}			
				_hiddenRows[_t] = false;	// Indicar si hay filas ocultas en la tabla	
										
				var _table = newLsTable(_t, _lsDataConf[_t]['table_type'], 1);
				if (_table != null) pushPage.addTable(_table, _t);	
			}	
		}  
	}
}

//////////////// Event Handlers for the Stock Tables ////////////////

/**************************************************************
 Description:
	Recoge la actualización del elemento
 
 Param:
 	- item: posición del elemento dentro del grupo
	- updateInfo: objeto que contiene los antiguos y nuevos valores del elemento
	- itemName: nombre del elemento	
**************************************************************/
var updateItem = function(item, updateInfo, itemName) {

	// Comprobar si hay actualización
	if (updateInfo == null) {
		return;
	}

	var _table = updateInfo.LS_QfY.LS_LSh;		
	var _dat = _lsDataConf[_table];
	
	// Comprobar si es una carga inicial o una actualización
	if (_dat['key_fx']){
	
		// Comprobar si hay que hacer transición de efectos
	     if ((_dat['fade']) && (_dat['fade'] == 'ON')) updateInfo.addField('#fade','ON',true);
		else updateInfo.addField('#fade','OFF',true);
	      
		var _oldValue = updateInfo.getOldValue(_dat['key_fx']);
		var _newValue = updateInfo.getNewValue(_dat['key_fx']);
		
		if (_oldValue == null) { // No hay datos anteriores --> 1ª carga de datos
//			updateInfo.addField('#fx_trend',_dat['fx']['fx_init']);	// Efecto inicial
			updateInfo.addField('#fx_trend','snapshot');
		} 
		else if (updateInfo.isValueChanged(_dat['key_fx'])) {	// Hay datos anteriores --> Actualización
			if (_oldValue > _newValue) {	// Bajada
				updateInfo.addField('#fx_trend',_dat['fx']['fx_down']);
			} 
			else if (_oldValue < _newValue){	// Subida
				updateInfo.addField('#fx_trend',_dat['fx']['fx_up']);
			}
		}
		else updateInfo.addField('#fx_trend',null);		
	}		
}

/**************************************************************
 Description:
	Formatea los datos y lanza los efectos definidos
 
 Param:
 	- item: posición del elemento dentro del grupo
	- itemUpdate: objeto que contiene los nuevos valores y los nuevos valores formateados
	- itemName: nombre del elemento	
**************************************************************/
var formatValues = function(item, itemUpdate, itemName) {					
	if (itemUpdate == null) {
		return;
	}
	var _table = itemUpdate.LS_QfY.LS_LSh;
	var _dat = _lsDataConf[_table];

	// Si hay paginación se fuerza el itemName para que se mantenga el de la tabla inicial
	if ((_dat['pagination']) && (_dat['pagination'] != false)) var itemName = 'item' + item;
	
	// Comprobar si hay transición de efectos
	if (itemUpdate.getServerValue('#fade') == 'ON') {
        itemUpdate.setHotToColdTime(300);
     }
	else itemUpdate.setHotTime(500);
	

	// CAMBIOS CSS ----------------
	// Comprobar cambios css
	if (_dat['fields_css']){			
		if (_dat['key_css']){
			var val = itemUpdate.getServerValue(_dat['key_css']);		
			if (val < 0) var _accion = 'css_down';
		 	else if (val > 0) var _accion = 'css_up';
			else var _accion = 'css_equal';

			for (var i=_dat['fields_css'].length-1; i>=0; i--){												
//				for (var i=0; i<_dat['fields_css'].length; i++){	
				var _item = _dat['fields_css'][i];	
				if (_dat['fields'][_item] && _dat['fields'][_item]['css_set']){	
					var _cssSetName = _dat['fields'][_item]['css_set'];							
					var _cssSet = _dat['css_set'][_cssSetName];
					
					if (_item.substring(0,1) == '_') {
						var _itemActual = $(_item + '_' + itemName);									
						if ((_itemActual) && (_itemActual.hasClass(_cssSet[_accion]) == false)){						
							if (_accion == 'css_down')
								_itemActual.removeClass(_cssSet['css_up']).removeClass(_cssSet['css_equal']).addClass(_cssSet[_accion]);
							else if (_accion == 'css_up')
								_itemActual.removeClass(_cssSet['css_down']).removeClass(_cssSet['css_equal']).addClass(_cssSet[_accion]);								
							else  if (_accion == 'css_equal')
								_itemActual.removeClass(_cssSet['css_down']).removeClass(_cssSet['css_up']).addClass(_cssSet[_accion]);								
						}
					
					}
					else	itemUpdate.setStyle(_item,_cssSet[_accion],_cssSet[_accion]);					
				}
			}				 	 
		 }				
	}
		
	// EFECTOS -------------------										
	// Comprobar si hay que lanzar efectos
	var _fx = itemUpdate.getServerValue('#fx_trend');
	if ((_fx != null) && (_fx != 'snapshot')){
		// tendencia		
		if (_dat['fields_fx_trend']){					
			if (_dat['fields_fx_trend'][0] == 'all'){
				itemUpdate.setRowAttribute(_fx,'',_dat['fx']['property']);	
			}
			else{	
				for (var i=_dat['fields_fx_trend'].length-1; i>=0; i--){					
//					for (var i=0; i<_dat['fields_fx_trend'].length; i++){				
					itemUpdate.setAttribute(_dat['fields_fx_trend'][i],_fx,'',_dat['fx']['property']);			
				}
			}
		}
	}
		
	if (_fx != 'snapshot'){				
		// aviso
		if (_dat['fields_fx_alert']){	
			for (var i=_dat['fields_fx_alert'].length-1; i>=0; i--){					
	//				for (var i=0; i<_dat['fields_fx_alert'].length; i++){
				itemUpdate.setAttribute(_dat['fields_fx_alert'][i],_dat['fx']['fx_alert'],'',_dat['fx']['property']);			
			}				 	 
		}		 	      
	}
			
	// FORMATEO -----------	
	if (_dat['fields']){
		for( var _el in _dat['fields'] ){
			var _newValue = itemUpdate.getFormattedValue(_el);
			if ((_newValue != null) && (_newValue != '')){	
				switch(_dat['fields'][_el]['type']){
					case 'number':
						if (_dat['fields'][_el]['base']) _newValue = _newValue/_dat['fields'][_el]['base'];
						var _formattedVal = numberFormat(_newValue, _dat['fields'][_el]['dec'], _dat['fields'][_el]['dec_sep'], _dat['fields'][_el]['ths_sep']);      
						if (_dat['fields'][_el]['extra']) _formattedVal += _dat['fields'][_el]['extra'];
						itemUpdate.setFormattedValue(_el,_formattedVal);
						break;
										
					case 'datetime':
						var _formattedVal = dateTimeFormat(_newValue, _dat['fields'][_el]['view']);
						itemUpdate.setFormattedValue(_el,_formattedVal);
						break;
					default:	// Aquí entra el tipo 'string'
				}			
			}
		}
	}
}
	
//////////////// Funciones de formateo //////////////////////

/**************************************************************
 Description:
	Función que formatea la fecha y/o la hora
 
 Param:
 	- val: valor a formatear	
 	- dat: [d,t,dt,...] indica si se quiere obtener la fecha, la hora o ambas
**************************************************************/
var dateTimeFormat = function(val, dat) {
	val = val.replace(" ","");	// Eliminar espacios en blanco
	var _longVal = val.length;
	if (_longVal > 10){	// Llega un dato del tipo DD/MM/AAHH:MM:SS
		switch(dat){
			case "d":		// DD/MM/AA
				var _dateTime = val.substring(0,8);
				break;
			case "d2":		// DD/MM
				var _dateTime = val.substring(0,5);
				break;				
			case "t":		//HH:MM:SS
				var _dateTime = val.substring(8,_longVal);
				break;
			case "t2":		//HH:MM
				var _dateTime = val.substring(5,_longVal);
				break;							
			case "dt":	// DD/MM/AA HH:MM:SS
				var _dateTime = val.substring(0,8) + ' ' + val.substring(8,_longVal);
				break;					
		}
	}
	else{	// Llega un dato del tipo DD/MM/AA, DD/MM/AAAA o HH:MM:SS
		var _dateTime = val;	
	}

	return _dateTime;
}

/**************************************************************
 Description:
	Función que formatea un número
 
 Param:
 	- number: número
	- decimales: nº de decimales
	- dec_point: caracter utilizado como separador decimal
	- thousands_sep: caracter utilizado como separador de miles
**************************************************************/
var numberFormat = function(number, decimals, dec_point, thousands_sep) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival

 	 /**************************************************************
	 Description:
		Función que corrije el funcionamiento del método toFixed()
	 
	 Param:
	 	- number: número
		- decimales: nº de decimales
	**************************************************************/
	var toFixedFixBug = function(number,decimals) {
		var d = Math.pow(10,decimals);
		var j = (Math.round(number*d)/d).toString().split('.');
		if (!isFinite(j[1])) {	// No hay decimales
			return parseFloat(j[0]).toFixed(decimals);
		}
		else{
			while(j[1].length < decimals) {j[1] += '0';} 
			return j[0]  + '.' + j[1];
		}			
	}
	
	var n = number, prec = decimals;
//	n = !isFinite(+n) ? 0 : +n;
	if (isFinite(+n)){	// Si 'n' es finito
		prec = !isFinite(+prec) ? 0 : Math.abs(prec);
		var sep = (typeof thousands_sep == 'undefined') ? ',' : thousands_sep;
		var dec = (typeof dec_point == 'undefined') ? '.' : dec_point;
	 
	//   var s = (prec > 0) ? n.toFixed(prec) : Math.round(n).toFixed(prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;
		var s = (prec > 0) ? toFixedFixBug(n,prec) : Math.round(n).toFixed(prec);
	
		var abs = Math.abs(n).toFixed(prec);
		var x, i;
		
		if (abs >= 1000) {
			x = abs.split(/\D/);
			i = x[0].length % 3 || 3;		
			x[0] = s.slice(0,i + (n < 0)) + x[0].slice(i).replace(/(\d{3})/g, sep+'$1');		
			s = x.join(dec);
		} else s = s.replace('.', dec);
	}
	else s = '';
	
	return s;
}

/**************************************************************
 Description:
	Lee una cookie
	
 Param:
 	- id: nombre de la cookie
 	
 Return:
 	Valor almacenado
 	
 NOTA: document.cookie devuelve una cadena con todas las cookies concatenadas: 
 id1=valor1;id2=valor2;...
**************************************************************/
var readCookie = function(id){
	var _cookies = document.cookie; 
	if(!_cookies) return false;
	var _start = _cookies.indexOf(id);
	if(_start == -1) return false;
	_start = _start + id.length+1;
	var _long = _cookies.indexOf("; ", _start) - _start; 
	if (_long<=0) _long = _cookies.length;
	
	return _cookies.substr(_start, _long);
}
