// ----------------------------------------------------------------------------
// ------------------ Librería de visualización de rutas ----------------------
// --------------------------------------------------------- (c) 2004 IDOM(JRF)

// ----------------------------------------------------------------------------
// Constantes.
// ----------------------------------------------------------------------------

// Espacio que va a ocupar una capa en la línea. Define la separación entre capas.
var ESPACIO_CAPA = 40;

// Retardo empleado al mover la bola.
var RETARDO = 300;

// Constantes de ajuste de la capa que se arrastra al puntero del ratón.
var MARGEN_HORIZONTAL = 5;
var MARGEN_VERTICAL = 5;

// Ajustes de las ventanas de datos de nodos y rutas. SI SE MODIFICAN LAS DIMENSIONES MODIFICAR TAMBIEN LA POSICION.
var AJUSTES_VENTANA_PARADAS_NUEVO = "width=375,height=360,left=" + ((screen.width - 375)/2) + ",top=" + ((screen.height - 360)/2) + ",resizable=no,menubar=no,toolbar=no,directories=no,location=no,scrollbars=no,status=no";
var AJUSTES_VENTANA_PARADAS_EDITAR = "width=375,height=455,left=" + ((screen.width - 375)/2) + ",top=" + ((screen.height - 455)/2) + ",resizable=no,menubar=no,toolbar=no,directories=no,location=no,scrollbars=no,status=no";
var AJUSTES_VENTANA_RUTAS = "width=330,height=160,left=" + ((screen.width - 330)/2) + ",top=" + (((screen.height - 50)/2) - 100) + ",resizable=no,menubar=no,toolbar=no,directories=no,location=no,scrollbars=no,status=no";

// Número de capas creadas para la inserción de nodos (no paradas).
// Limitan el número máximo de nodos que se pueden crear sin recargar la página.
var CAPAS_RESERVADAS_INSERCION_NODOS = 50;

// Definen la coordenada superior izquierda en la que se
// visualizan los sectores del mapa por la pantalla.
var SECTOR_POSX = 285;
var SECTOR_POSY = 80;

// Módulo de la aplicación en el que nos encontramos.
var GESTIONAR_PARADAS = 0;
var GESTIONAR_NODOS = 1;
var GESTIONAR_RUTAS = 2;
var VER_RUTAS = 3;
var BUSQUEDA = 4;

// Tipo de operación concreta que se estamos realizando en un momento dado.
var BORRAR_PARADAS = 0;
var BORRAR_NODOS = 1;
var INSERTAR_PARADAS = 2;
var INSERTAR_NODOS = 3;
var MOVER_PARADAS = 4;
var MOVER_NODOS = 5;
var INSERTAR_RUTAS = 6;

// Imágenes de nodos y paradas.
var IMG_PARADA_DESELECCIONADA = "parada.gif";
var IMG_PARADA_SELECCIONADA = "parada_seleccionada.gif";
var IMG_PARADA_MOVIMIENTO = "parada_movimiento.gif";
var IMG_NODO_DESELECCIONADO = "nodo.gif";
var IMG_NODO_SELECCIONADO = "nodo_seleccionado.gif";
var IMG_NODO_MOVIMIENTO ="nodo_movimiento.gif";
var IMG_PARADA_FUERA_DE_SERVICIO = "parada_fuera_de_servicio.gif";
var IMG_NODO_RUTA = "nodo";


// Tipos de indicaciones a mostrar en el mapa.
var INDICACION_HASTA_TILOS = 1;
var INDICACION_DESDE_TILOS = 2;
var INDICACION_DESDE_ARINS = 3;
var INDICACION_HASTA_ARINS = 4;
var INDICACION_DESDE_BARCIA = 5;
var INDICACION_HASTA_BARCIA = 6;


// ----------------------------------------------------------------------------
// Variables.
// ----------------------------------------------------------------------------
var bArrastraCapa = false;								// Indica si hay que arrastrar la capa activa.
var nNuevoNodoActual = 0;									// Indica el orden del nuevo nodo que estamos insertando.
var nOperacionActual;	  	  							// Indica la operacion concreta que se está realizando dentro de un módulo de la aplicación.
var nNodoIdEnMovimiento; 									// Capa que se está moviendo en estos momentos.
var objCapasEnSectorActivo = new Array(); // Capas que se están visualizando en el sector activo para simular el movimiento por pantalla.
var nContCapasSectorActivo;								// Contador para rellenar el vector de capas en el sector activo.
var tmrBola;															// Temporizador del movimiento al presentar la ruta.

// Captura de eventos de ratón (los nombres de los eventos van en obligatoriamente en minúsculas).
//document.onmousedown = visualizaCoordenadas;
document.onmousemove = alMoverRaton;
document.onmouseup = alSoltarBoton;
document.oncontextmenu = alPulsarBotonDerecho;

// ----------------------------------------------------------------------------
// Funciones.
// ----------------------------------------------------------------------------

// Constructor del objeto que almacena los datos de cada nodo.
function ObjNodo(nId, nPosX, nPosY, nSector, nTipo, bFueraDeServicio)
{
	this.nId = nId;
	this.nPosX = nPosX;
	this.nPosY = nPosY;
	this.nSector = nSector;
	this.nTipo = nTipo;
	this.bMovido = false; // Indica si el nodo se ha movido.
	this.bFueraDeServicio = bFueraDeServicio;
}

// Constructor del objeto que almacena los datos de cada ruta.
function ObjRuta(nRutaId, sNombre, nColor, nRutaDeVuelta)
{
	this.nRutaId = nRutaId;
	this.sNombre = sNombre;
	this.nColor = nColor;
	this.nRutaDeVuelta = nRutaDeVuelta;
}

// Constructor del objeto que almacena los datos de cada nodo en la visualización de rutas.
function ObjNodoBusqueda(nId, nPosX, nPosY, nSector, nTipo, nRutaId, nColor, nVueltaId)
{
	this.nId = nId;
	this.nPosX = nPosX;
	this.nPosY = nPosY;
	this.nSector = nSector;
	this.nTipo = nTipo;
	this.nRutaId = nRutaId;
	this.nColor = nColor;
	this.nVueltaId = nVueltaId;
}

// Nodo auxliar empleado para calcular las rutas.
function ObjNodoTransbordo(nId, nRutaLlegada, nRutaSalida)
{
	this.nId = nId;
	this.nRutaLlegada = nRutaLlegada;
	this.nRutaSalida = nRutaSalida;
}

// Constructor del objeto que almacena los datos básicos de cada parada.
function ObjParada(nId, sNombre)
{
	this.nId = nId;
	this.sNombre = sNombre;
}

// Constructor.
function Id_Posicion(nId, nPosX, nPosY)
{
	this.nId = nId;
	this.nPosX = nPosX;
	this.nPosY = nPosY;
}

// ----------------------------------------------------------------------------
// Captura el evento producido cuando se pulsa el botón derecho del ratón.
// ----------------------------------------------------------------------------
function alPulsarBotonDerecho()
{
	// Al pulsar el botón derecho damos por finalizada la inserción de nodos.	
	if (nOperacionActual == INSERTAR_NODOS)
		bArrastraCapa = false;

	return(false);
}

// ----------------------------------------------------------------------------
// Captura el evento producido cuando se suelta el botón del ratón.
// ----------------------------------------------------------------------------
function alSoltarBoton()
{
	var wndDatosParada; // Ventana donde se presentan los datos de una parada.
	var nPos;						// Posición dentro del vector de nodos de un nodo particular.

	// Si hemos soltado dentro de los límites del mapa.
	if (bArrastraCapa && bEstaEnLosLimitesDelMapa(event.x, event.y))
	{
		switch(nOperacionActual)
		{
			// Si estamos insertando nodos, dejamos fijo el actual y pasamos a mover el siguiente.
			case INSERTAR_NODOS: nNuevoNodoActual++;break;

			// Si estamos insertando paradas, dejamos fija la actual y pasamos a pedir sus datos.
			case INSERTAR_PARADAS:
			{
				bArrastraCapa = false;
				wndDatosParada = window.open("datos_parada.asp?nPosX=" + (event.x - MARGEN_HORIZONTAL - SECTOR_POSX) + "&nPosY=" + (event.y - MARGEN_VERTICAL - SECTOR_POSY) + "&nSector=" + document.frm.txtSectorActivo.value, "datosNodo", AJUSTES_VENTANA_PARADAS_NUEVO);
			}break;

			// Si estamos moviendo paradas confirmamos si deseamos moverla a la posición actual.
			case MOVER_PARADAS:
			{
				// Si aceptamos presentamos la ventana de datos de la parada.
				if(confirm("¿Desea mover la parada a esta posición?"))
				{
					wndDatosParada = window.open("datos_parada.asp?nNodoId=" + nNodoIdEnMovimiento + "&nPosX=" + (event.x - MARGEN_HORIZONTAL - SECTOR_POSX) + "&nPosY=" + (event.y - MARGEN_VERTICAL - SECTOR_POSY) + "&nSector=" + document.frm.txtSectorActivo.value , "datosNodo", AJUSTES_VENTANA_PARADAS_EDITAR);
					cambiaImagen(nNodoIdEnMovimiento, INSERTAR_PARADAS);
				}

				// Deja la capa donde estaba.
				else
				{
					// Obtenemos la posición original del nodo en movimiento y la mostramos.
					bArrastraCapa = false;
					nPos = obtenPosicion(objNodos, nNodoIdEnMovimiento);
					cambiaImagen(nNodoIdEnMovimiento, INSERTAR_PARADAS);
					muestraCapa(nNodoIdEnMovimiento, objNodos[nPos].nPosX, objNodos[nPos].nPosY);
					nNodoIdEnMovimiento = -1;
				}
			}break;

			// Si estamos moviendo nodos.
			case MOVER_NODOS:
			{
				// Actualizamos los datos del nodo movido.
				nPos = obtenPosicion(objNodos, nNodoIdEnMovimiento);
				objNodos[nPos].nPosX = event.x - MARGEN_HORIZONTAL - SECTOR_POSX;
				objNodos[nPos].nPosY = event.y - MARGEN_VERTICAL - SECTOR_POSY;
				objNodos[nPos].bMovido = true;
				cambiaImagen(nNodoIdEnMovimiento, INSERTAR_NODOS);
				nNodoIdEnMovimiento = -1;
			}
		}
	}
}

// ----------------------------------------------------------------------------
// Captura el evento producido cuando se mueve el ratón.
// ----------------------------------------------------------------------------
function alMoverRaton()
{
	if (bArrastraCapa)
	{
		switch(nOperacionActual)
		{
			case INSERTAR_NODOS: muestraCapa("Nuevo" + nNuevoNodoActual, event.x - MARGEN_HORIZONTAL, event.y - MARGEN_VERTICAL);break;
			case INSERTAR_PARADAS: muestraCapa("Nueva", event.x - MARGEN_HORIZONTAL, event.y - MARGEN_VERTICAL);break;
			case MOVER_PARADAS: case MOVER_NODOS:	muestraCapa(nNodoIdEnMovimiento, event.x - MARGEN_HORIZONTAL, event.y - MARGEN_VERTICAL);
		}
	}
}

// ----------------------------------------------------------------------------
// Permite la creación de nuevos nodos en el mapa.
// ----------------------------------------------------------------------------
function creaNuevosNodos()
{
	muestraCapa("Nuevo" + nNuevoNodoActual, event.x - MARGEN_HORIZONTAL, event.y - MARGEN_VERTICAL);
	nOperacionActual = INSERTAR_NODOS;
	bArrastraCapa = true;
}

// ----------------------------------------------------------------------------
// Permite la creación de una nueva parada en el mapa.
// ----------------------------------------------------------------------------
function creaNuevaParada()
{
	muestraCapa("Nueva", event.x - MARGEN_HORIZONTAL, event.y - MARGEN_VERTICAL);
	nOperacionActual = INSERTAR_PARADAS;
	bArrastraCapa = true;
}

// ----------------------------------------------------------------------------
// Guarda los nodos creados en el mapa.
// ----------------------------------------------------------------------------
function guardaNuevosNodos()
{
	var sUrl;											// Url para la recarga de la página de gestión de nodos.
	var nIzq, nArr; 							// Coordenadas de los nodos a insertar.
	var bQuedanCapasPorInsertar;	// Bandera de fin de iteraciones.
	var nCont2 = 0;								// Contador en bucles.

	sUrl ="gestion_nodos.asp?nSector=" + document.frm.txtSectorActivo.value + "&";
	bQuedanCapasPorInsertar = true;
	for(var nCont = 0; nCont <= CAPAS_RESERVADAS_INSERCION_NODOS && bQuedanCapasPorInsertar; nCont++)
	{
		// Obtiene las coordenadas de la capa.
		nIzq = obtenCoordenadaIzquierda("Nuevo" + nCont) - (MARGEN_HORIZONTAL + SECTOR_POSX);
		nArr = obtenCoordenadaSuperior("Nuevo" + nCont) - (MARGEN_VERTICAL + SECTOR_POSY);

		// Si la coordenada izquierda es positiva es que su nodo correspondiente se ha insertado.
		// Por tanto, se añaden sus datos a la URL.
		if (nIzq > 0)
			sUrl = sUrl + "cp" + nCont + "=" + nIzq + "," + nArr + "&";

		// No se han insertado más capas.
		else
			bQuedanCapasPorInsertar = false;
	}

	// Se recarga la página de gestión de nodos con la url que hemos compuesto.
	document.location.replace(sUrl);

	// Recorre los nodos para ver cuales se han movido.
	sUrl ="gestion_nodos.asp?nMover=1&nSector=" + document.frm.txtSectorActivo.value + "&";
	for(var nCont = 0; nCont < objNodos.length; nCont++)
	{
		if (objNodos[nCont].bMovido)
		{
			sUrl = sUrl + "cp" + nCont2 + "=" + objNodos[nCont].nId + "," + (objNodos[nCont].nPosX - MARGEN_HORIZONTAL) + "." + (objNodos[nCont].nPosY - MARGEN_VERTICAL) + "&";
			nCont2++;
		}
	}

	// Se vuelve a recargar la página para actualizar los nodos que se han movido.
	if (nCont2 != 0)
		document.location.replace(sUrl);
}

// Visualiza las coordenadas donde se pulsa el ratón.
function visualizaCoordenadas()
{
	alert("("+ event.x + "," + event.y + ")");
}

// ----------------------------------------------------------------------------
// Crea las capas precisas para visualizar nodos o rutas.
// ----------------------------------------------------------------------------
function creaCapas(objNodos, nModuloActual)
{
	var nCont; 											// Contador para bucle.
	var nCapasDiagonal; 						// Número de capas precisas para unir dos nodos contiguos.
	var nX1, nY1, nX2, nY2; 				// Coordenadas absolutas de nodos.
	var objNodosTemp = new Array(); // Vector temporal para almacenar nodos.
	var nContTemp; 									// Contador para recorrer el vector temporal.
	var idNodo, idRuta;

	// Si se van a presentar rutas, calculamos las capas necesarias.
	if (nModuloActual == VER_RUTAS || nModuloActual == BUSQUEDA)
	{
		// Contador para el vector temporal.
		nContTemp = 0;

		// Recorre los vectores de coordenadas calculando el número de capas necesarias para marcar la ruta.
		for (nCont = 0; nCont < objNodos.length - 1; nCont++)
		{
			// Pasa el nodo al vector auxiliar.
			objNodosTemp[nContTemp++] = objNodos[nCont];

			// Calculamos las coordenadas absolutas de los puntos.
			nX1 = obtenCoordenadaAbsolutaX(objNodos[nCont].nSector, objNodos[nCont].nPosX);
			nY1 = obtenCoordenadaAbsolutaY(objNodos[nCont].nSector, objNodos[nCont].nPosY);
			nX2 = obtenCoordenadaAbsolutaX(objNodos[nCont + 1].nSector, objNodos[nCont + 1].nPosX);
			nY2 = obtenCoordenadaAbsolutaY(objNodos[nCont + 1].nSector, objNodos[nCont + 1].nPosY);

			// Calculamos el número de capas necesarias para cubrir la diagonal.
			nCapasDiagonal = Math.ceil(nObtenDiagonal(nX1, nY1, nX2, nY2) / ESPACIO_CAPA);

			// Pasamos al vector auxiliar los datos de las capas creadas. Así obtenemos un vector con todos los
			// nodos creados y no solo los devueltos por la BD. El id de cada capa de relleno se compone con el
			// identificador del nodo obtenido de la BD que le precede, seguido de un guión y un número de orden.
			for(var nCont2 = 0; nCont2 < nCapasDiagonal; nCont2++)
			{
				idNodo = (objNodos[nCont].nId + "_" + nCont2);
				idRuta = objNodos[nCont].nRutaId;
				
				// ----------------------------------------------------------
				// CONTROL DE NODOS DESPLAZADOS.
				// ----------------------------------------------------------
				
				if ((idRuta != "222" || idNodo.lastIndexOf("3560_") == -1) &&
						(idRuta != "224" || idNodo.lastIndexOf("3477_") == -1) &&
						(idRuta != "233" || idNodo.lastIndexOf("3575_") == -1) &&
						(idRuta != "238" || idNodo.lastIndexOf("3803_") == -1) &&
						(idRuta != "239" || idNodo.lastIndexOf("3560_") == -1) &&
						(idRuta != "240" || idNodo.lastIndexOf("3477_") == -1) &&
						(idRuta != "240" || idNodo.lastIndexOf("3159_") == -1) &&
						(idRuta != "247" || idNodo.lastIndexOf("3477_") == -1) &&
						(idRuta != "248" || idNodo.lastIndexOf("3560_") == -1) &&
						
						(idRuta != "249" || idNodo.lastIndexOf("3560_") == -1) &&
						(idRuta != "249" || idNodo.lastIndexOf("2640_") == -1) &&
						
						(idRuta != "252" || idNodo.lastIndexOf("3477_") == -1) &&
						(idRuta != "253" || idNodo.lastIndexOf("3935_") == -1) &&
						(idRuta != "256" || idNodo.lastIndexOf("3813_") == -1) &&
						
						(idRuta != "233" || idNodo.lastIndexOf("2640_") == -1) &&
						
						(idRuta != "244" || idNodo.lastIndexOf("3614_") == -1))
						
						
					objNodosTemp[nContTemp++] = new ObjNodoBusqueda(idNodo, 0, 0, 0, 3, idRuta, objNodos[nCont].nColor, 0);
					
				// ----------------------------------------------------------
			}
		}

		// Pasa el último de los nodos.
		objNodosTemp[nContTemp] = objNodos[objNodos.length - 1];

		// Renombramos el vector auxiliar.
		objNodos = objNodosTemp;
	}

  // Crea las capas.
	for (nCont = 0; nCont < objNodos.length; nCont++)
	{
		// Declara la capa si no existía ya. Esto se hace porque una ruta puede pasar varias veces
		// por el mismo nodo y tendríamos dos capas con el mismo nombre.
		if(!existeCapa(objNodos[nCont].nId))
		{
			// Asignamos un icono a presentar por pantalla según el tipo de nodo.
			switch (objNodos[nCont].nTipo)
			{
				// Parada.
				case 1:
				{
					// Si la parada está fuera de servicio.
					if (objNodos[nCont].bFueraDeServicio == 1)
						src = "src='imagenes/nodos/" + IMG_PARADA_FUERA_DE_SERVICIO + "'>";
					else
						src = "src='imagenes/nodos/" + IMG_PARADA_DESELECCIONADA + "'>";
				}break;

				// Nodo o relleno.
				case 2: case 3:
				{
					// Si vamos a presentar una ruta.
					if (nModuloActual == VER_RUTAS || nModuloActual == BUSQUEDA)
						src = "src='imagenes/nodos/" + IMG_NODO_RUTA + objNodos[nCont].nColor + ".gif'>";
					else
						src = "src='imagenes/nodos/" + IMG_NODO_DESELECCIONADO+ "'>";
				}
			}

			document.write("<div id=cp" + objNodos[nCont].nId + " style='visibility:hidden;position:absolute;border:none;left:0;top:0' ");

			// Según el módulo de la aplicación en que nos encontremos.
			switch(nModuloActual)
			{
				case GESTIONAR_PARADAS: document.write("onClick='seleccionaNodo(" + objNodos[nCont].nId + ", BORRAR_PARADAS);' onContextMenu='permiteMoverNodo(" + objNodos[nCont].nId + ", MOVER_PARADAS);' onDblClick='editaDatosParada(" + objNodos[nCont].nId + ", " + nModuloActual + ");'");break;
				case GESTIONAR_NODOS: document.write("onClick='seleccionaNodo(" + objNodos[nCont].nId + ", BORRAR_NODOS);' onContextMenu='permiteMoverNodo(" + objNodos[nCont].nId + ", MOVER_NODOS);'");break;
				case GESTIONAR_RUTAS:	document.write("onClick='seleccionaNodo(" + objNodos[nCont].nId + ", GESTIONAR_RUTAS);'");break;
				case BUSQUEDA: case VER_RUTAS:
				{
					// Si es una parada.
					if (objNodos[nCont].nTipo == 1)
						document.write("onMouseOver=cambiaPuntero('hand') onMouseOut=cambiaPuntero('default') onDblClick='editaDatosParada(" + objNodos[nCont].nId + ", " + nModuloActual + ");'");
				}
			}

			document.write("><img id = 'img" + objNodos[nCont].nId + "' alt='" + objNodos[nCont].nId + "' " + src + "</div>");
		}

	} // Crea las capas.

	return(objNodos);
}

// ----------------------------------------------------------------------------
// Si nos encontramos en el módulo de gestión de nodos o paradas 
// se permite cambiar su localización dentro del plano.
// ----------------------------------------------------------------------------
function permiteMoverNodo(nNodoId, nOperacion)
{
	var nPos; // Posición de un nodo dentro del array de nodos.

	// Si pulsamos botón para dejar de arrastrar un nodo.
	if (bArrastraCapa)
	{
		if (bEstaEnLosLimitesDelMapa(event.x, event.y))
			bArrastraCapa = false;
	}
	// Si pulsamos botón para arrastrar un nodo.
	else
	{
		// En la gestión de nodos no se permite mover paradas.
		nPos = obtenPosicion(objNodos, nNodoId);
		if (objNodos[nPos].nTipo !=1 || nOperacion != MOVER_NODOS)
		{
			// Borra elemento del combo por si acaso, cambia su icono y permite arrastrarlo.
			borraElementoCombo(document.frm.cmbNodosABorrar, nNodoId);
			cambiaImagen(nNodoId, nOperacion);
			bArrastraCapa = true;
			nOperacionActual = nOperacion;
			nNodoIdEnMovimiento = nNodoId;
		}
	}
}

// ----------------------------------------------------------------------------
// Selecciona un nodo en el mapa.
// ----------------------------------------------------------------------------
function seleccionaNodo(nId, nOperacion)
{
	var nPos; // Posición del nodo dentro del array.

	// Según la operación que estemos realizando.
	switch(nOperacion)
	{
		case GESTIONAR_RUTAS:
		{
			// Si estamos marcando una nueva ruta.
			if (!document.frm.btnFinRuta.disabled)
			{
				// Cambia la imagen del nodo seleccionado y lo añade a la lista.
				nPos = obtenPosicion(objNodos, nId)
				if (objNodos[nPos].bFueraDeServicio == 0)
				{
					cambiaImagen(nId, INSERTAR_RUTAS);
					insertaElementoCombo(document.frm.cmbNodosNuevaRuta, nId);
				}
				else
					alert("Esta parada está fora de servicio.\nNon se pode engadir á ruta.");
			}
		}break;

		case BORRAR_PARADAS:
		{
			// Cambia la imagen y lo inserta/elimina de la lista
			// según  se haya seleccionado o deseleccionado.
			if(!bArrastraCapa)
			{
				if(cambiaImagen(nId, nOperacion))
					insertaElementoCombo(document.frm.cmbNodosABorrar, nId);
				else
					borraElementoCombo(document.frm.cmbNodosABorrar, nId);
			}
		}break;
		
		case BORRAR_NODOS:
		{
			// Si no estamos moviendo el nodo.
			if(!bArrastraCapa)
			{
				nPos = obtenPosicion(objNodos, nId);
				if (objNodos[nPos].nTipo != 1) // No se permiten seleccionar paradas en la gestión de nodos.
				{
					// Cambia la imagen y lo inserta/elimina de la lista
					// según  se haya seleccionado o deseleccionado.
					if(cambiaImagen(nId, nOperacion))
						insertaElementoCombo(document.frm.cmbNodosABorrar, nId);
					else
						borraElementoCombo(document.frm.cmbNodosABorrar, nId);
				}
			}
		}
	}	// Según la operación que estemos realizando.
}

// ----------------------------------------------------------------------------
// Abre el formulario de datos del nodo para modificar.
// ----------------------------------------------------------------------------
function editaDatosParada(nNodoId, nModuloActual)
{	
	var wndDatosParada; // Ventana de datos de la parada.
	
	// Según el módulo actual
	if (nModuloActual == GESTIONAR_PARADAS)
	{
		// Si no estamos moviendo la parada de sitio.
		if(!bArrastraCapa)
		{
			borraElementoCombo(document.frm.cmbNodosABorrar, nNodoId);
			cambiaImagen(nNodoId,INSERTAR_PARADAS);
			wndDatosParada = window.open("datos_parada.asp?nNodoId=" + nNodoId, "datosNodo", AJUSTES_VENTANA_PARADAS_EDITAR);
		}
	}
	else if (nModuloActual == VER_RUTAS || nModuloActual == BUSQUEDA)
		wndDatosParada = window.open("datos_parada.asp?nNodoId=" + nNodoId + "&bSoloLectura=si", "datosNodo", AJUSTES_VENTANA_PARADAS_EDITAR);
}

// ----------------------------------------------------------------------------
// Abre el formulario de datos de la ruta.
// ----------------------------------------------------------------------------
function editaDatosRuta(nRutaId, nModuloActual)
{	
	var wndDatosRuta; // Ventana de datos de la ruta.

	// Según el módulo actual.
	if (nModuloActual == GESTIONAR_RUTAS)
		wndDatosRuta = window.open("datos_ruta.asp?nRutaId=" + nRutaId, "datosRuta", AJUSTES_VENTANA_RUTAS);

	//else if (nModuloActual == VER_RUTAS)
	//	wndDatosParada = window.open("datos_parada.asp?nNodoId=" + nNodoId + "&bSoloLectura=si", "datosNodo", AJUSTES_VENTANA_RUTAS);
}

// ----------------------------------------------------------------------------
// Crea una nueva ruta.
// ----------------------------------------------------------------------------
function creaNuevaRuta()
{
	// Se indica que se está creando una nueva ruta.
	document.frm.btnFinRuta.disabled = false;
	document.frm.btnNuevo.disabled = true;

	// Solicita un nombre de ruta.
	var wndDatosNodo = window.open("datos_ruta.asp", "datosRuta", AJUSTES_VENTANA_RUTAS);
}

// ----------------------------------------------------------------------------
// Dá por finalizada la creación de una nueva ruta.
// ----------------------------------------------------------------------------
function finalizaNuevaRuta()
{	
	// Se indica que se está creando una nueva ruta.
	document.frm.btnFinRuta.disabled = true;
	document.frm.btnNuevo.disabled = false;
}

// ----------------------------------------------------------------------------
// Simula movimiento a lo largo de la ruta mostrada.
// ----------------------------------------------------------------------------
function mueveBola()
{
	// Desresalta el nodo anterior.
	resaltaNodo(objCapasEnSectorActivo[nContCapasSectorActivo]);

	// Si no se ha llegado al final del recorrido, se pasa a la siguiente bola.
	if (nContCapasSectorActivo < (objCapasEnSectorActivo.length - 1))
		nContCapasSectorActivo++;

	// Si se ha llegado al final, se repite el ciclo.
	else
	  nContCapasSectorActivo = 0;

	// Resalta el nodo actual.
	resaltaNodo(objCapasEnSectorActivo[nContCapasSectorActivo]);

	// Llamada recursiva a esta función cada RETARDO milisegundos para simular el movimiento.
	tmrBola = setTimeout("mueveBola()", RETARDO);
}

function resaltaNodo(nNodoId)
{
	var src;

	// Por si cambiamos a un sector sin nodos y el movimiento de la bola
	// todavía no se ha detenido.
	try
	{
		var sRuta = document.images["img" + nNodoId].src;
		var sNombre = sRuta.substring(sRuta.lastIndexOf("/") + 1, sRuta.length);

		if (sNombre.substr(0, 4) == "sel_")
			src = "imagenes/nodos/" + sNombre.substr(4, sNombre.length - 4);
		else
			src = "imagenes/nodos/sel_" + sNombre;

		document.images["img" + nNodoId].src = src;
	}
	catch(e)
	{}
}

// ----------------------------------------------------------------------------
// Visualiza la ruta en el mapa y marca el recorrido.
// ----------------------------------------------------------------------------
function muestraRuta(nSectorActivo)
{
	var nX1, nY1, nX2, nY2; 						// Coordenadas de los nodos extremos de la línea.
	var nIncX, nIncY;										// Incremento de cada nodo respecto al anterior.
	var objNodosBD = new Array(); 			// Vector temporal para almacenar nodos.
	var nSectorOrigen, nSectorDestino;	// Sectores origen y destino de la línea.
	var nDiagonal;											// Longitud de la diagonal entre dos puntos.
	var nMostrarIndicacion;							// Indica si hay que presentar alguna indicación adicional acerca de la ruta presentada.

	// Detiene el movimiento que marca el sentido de la ruta.
	clearTimeout(tmrBola);

	// Limpia el mapa.
	ocultaTodasLasCapas(objNodos);
	ocultaTodasLasIndicaciones();

	// Elimina el array de capas del sector activo.
	objCapasEnSectorActivo.length = 0;

	// Muestra las paradas que no forman parte de la ruta.
	muestraParadas(objParadas, nSectorActivo);

	// Extraemos los nodos de la ruta que no son de relleno.
	for(var nCont1 = 0, nCont2 = 0; nCont1 < objNodos.length; nCont1++)
		if (objNodos[nCont1].nTipo != 3)
			objNodosBD[nCont2++] = objNodos[nCont1];

	// Si hay ruta que mostrar.
	nMostrarIndicacion = 0; // Por defecto no se muestra ninguna indicación acerca de la ruta.
	if (objNodosBD.length != 0)
	{	
		// Muestra la ruta completa.
		nContCapasSectorActivo = 0;
		for (var nCont = 0; nCont < objNodosBD.length - 1; nCont++)
		{
			// Obtiene los sectores a los que pertenecen el nodo origen y el destino.
			nSectorOrigen = objNodosBD[nCont].nSector;
			nSectorDestino = objNodosBD[nCont + 1].nSector;

			// Calculamos las coordenadas absolutas por si alguno de los nodos no está en el sector activo.		
			nX1 = obtenCoordenadaAbsolutaX(nSectorOrigen, objNodosBD[nCont].nPosX - SECTOR_POSX);
			nY1 = obtenCoordenadaAbsolutaY(nSectorOrigen, objNodosBD[nCont].nPosY - SECTOR_POSY);
			nX2 = obtenCoordenadaAbsolutaX(nSectorDestino, objNodosBD[nCont + 1].nPosX - SECTOR_POSX);
			nY2 = obtenCoordenadaAbsolutaY(nSectorDestino, objNodosBD[nCont + 1].nPosY - SECTOR_POSY);

			// Calculamos el incremento a aplicar según las coordenadas globales.
			nDiagonal = nObtenDiagonal(nX1, nY1, nX2, nY2);
			nIncX = ((nX2 - nX1) / nDiagonal) * ESPACIO_CAPA;
			nIncY = ((nY2 - nY1) / nDiagonal) * ESPACIO_CAPA;

			// Si el primer nodo está en el sector activo.
			if (nSectorOrigen == nSectorActivo)
				muestraLinea(objNodosBD[nCont], objNodosBD[nCont + 1], nIncX, nIncY, true);

			// Si el primer nodo no está en el sector activo y el segundo si.
			else if ((nSectorOrigen != nSectorActivo) && (nSectorDestino == nSectorActivo))
				muestraLinea(objNodosBD[nCont], objNodosBD[nCont + 1], -nIncX, -nIncY, false);

			// ---------------------------------------------------------------
			// Según el sector activo y la ruta mostrada, se comprueba si hay 
			// que presentar alguna indicación adicional sobre el mapa.
			// ---------------------------------------------------------------
			if (nSectorActivo == 155)
			{
				if (objNodosBD[nCont].nRutaId == 228 || objNodosBD[nCont].nRutaId == 238)
					nMostrarIndicacion = INDICACION_HASTA_TILOS;
				else if(objNodosBD[nCont].nRutaId == 229 || objNodosBD[nCont].nRutaId == 237)
					nMostrarIndicacion = INDICACION_DESDE_TILOS;
			}
			else if (nSectorActivo == 126)
			{
				if (objNodosBD[nCont].nRutaId == 230)
					nMostrarIndicacion = INDICACION_DESDE_ARINS;
				else if(objNodosBD[nCont].nRutaId == 231)
					nMostrarIndicacion = INDICACION_HASTA_ARINS;
			}
			else if (nSectorActivo == 121)
			{
				if (objNodosBD[nCont].nRutaId == 232)
					nMostrarIndicacion = INDICACION_DESDE_BARCIA;
				else if(objNodosBD[nCont].nRutaId == 233)
					nMostrarIndicacion = INDICACION_HASTA_BARCIA;
			}
		}

		// Si el último nodo de la ruta pertenece al sector activo, lo pinta.
		if (objNodosBD[nCont].nSector == nSectorActivo)
		{
			muestraCapa(objNodosBD[nCont].nId, objNodosBD[nCont].nPosX, objNodosBD[nCont].nPosY);
			objCapasEnSectorActivo[nContCapasSectorActivo++] = objNodosBD[nCont].nId;
		}

		// Asignamos la imagen por defecto a todos los nodos por si alguno quedó resaltado
		// en la última visualización.
		for(var nX=0; nX < objCapasEnSectorActivo.length; nX++)
		{
			var sRuta = document.images["img" + objCapasEnSectorActivo[nX]].src;
			var sNombre = sRuta.substring(sRuta.lastIndexOf("/") + 1, sRuta.length);

			if (sNombre.substr(0, 4) == "sel_")
				src = "imagenes/nodos/" + sNombre.substr(4, sNombre.length - 4);
			else
				src = "imagenes/nodos/" + sNombre;

			document.images["img" + objCapasEnSectorActivo[nX]].src = src;
		}

		// Si hay que mostrar una indicación acerca de la ruta.
		switch(nMostrarIndicacion)
		{
			case 1: muestraCapa("HastaTilos", 640, 480);break;
			case 2: muestraCapa("DesdeTilos", 640, 480);break;
			case 3: muestraCapa("HastaArins", 410, 90);break;
			case 4: muestraCapa("DesdeArins", 410, 90);break;
			case 5: muestraCapa("HastaBarcia", 320, 205);break;
			case 6: muestraCapa("DesdeBarcia", 320, 205);break;

		}

		// Simula el movimiento de la ruta.
		nContCapasSectorActivo = 0;
		resaltaNodo(objCapasEnSectorActivo[0]);
		tmrBola = setTimeout("mueveBola()", RETARDO);

	} // Si hay ruta que mostrar.
}

// Muestra los nodos correspondientes al sector seleccionado.
function muestraNodos(nSector)
{
	// Recorre los nodos.
	for (var nCont = 0; nCont < objNodos.length; nCont++)
	{
		// Si el nodo pertenece al sector pasado como parámetro. Lo muestra y lo trae a primer plano.
		if (objNodos[nCont].nSector == nSector)
			muestraCapa(objNodos[nCont].nId, objNodos[nCont].nPosX, objNodos[nCont].nPosY);

		// Si no pertenece al sector.
		else
		{
			// Lo oculta.
			ocultaCapa(objNodos[nCont].nId);
		}
	}
}

// Traza una línea de capas entre el punto (nOrigenX, nOrigenY) al punto (nDestinoX, nDestinoY).
function muestraLinea(objNodoOrigen, objNodoDestino, nIncX, nIncY, bPrimeroDentro)
{
	var nCoordX, nCoordY;  	// Coordenadas donde se va a visualizar cada capa.
	var nOrigenX, nOrigenY;	// Coordenadas de origen de la ruta.
	var nContNodos;					// Contador de nodos efectivamente visualizados en el sector activo.
	var objNodosAux = new Array(); // Vector de nodos cuando el origen de la línea está fuera del sector activo.
	var nX;

	// Obtiene las posiciones, dentro del vector de nodos, de los nodos pasados como parámetros.
	var nPosOrigen = obtenPosicion(objNodos, objNodoOrigen.nId);
	var nPosDestino = obtenPosicion(objNodos, objNodoDestino.nId);

	// Si el punto de origen de la línea se encuentra en el sector activo.
	if (bPrimeroDentro)
	{
		nOrigenX = objNodoOrigen.nPosX;
		nOrigenY = objNodoOrigen.nPosY;
	}

	// Si el punto de origen de la línea está fuera del sector activo.
	else
	{
		nOrigenX = objNodoDestino.nPosX;
		nOrigenY = objNodoDestino.nPosY;
	}

	// Pinta las capas de la línea.
	nContNodos = 0;

	// Si el punto de origen de la línea se encuentra en el sector activo.
	if (bPrimeroDentro)
	{
		for (nCont = nPosOrigen; nCont < nPosDestino - 1; nCont++)
		{
			nCoordX = nOrigenX + (nContNodos * nIncX);
			nCoordY = nOrigenY + (nContNodos * nIncY);
			nContNodos++;

			// Si la posición de los límites está dentro del sector, muestra la capa.
			if (bEstaEnLosLimitesDelMapa(nCoordX, nCoordY))
			{
				muestraCapa(objNodos[nCont].nId, nCoordX, nCoordY);
				objCapasEnSectorActivo[nContCapasSectorActivo++] = objNodos[nCont].nId;
			}
		}
	}

	// Si el punto de origen de la línea está fuera del sector activo.
	else
	{
		nX = 0;
		for (nCont=nPosDestino; nCont > nPosOrigen + 1; nCont--)
		{		
			nCoordX = nOrigenX + (nContNodos * nIncX);
			nCoordY = nOrigenY + (nContNodos * nIncY);
			nContNodos++;

			// Almacenamos los nodos con coordenadas válidas en un vector auxiliar.
			if (bEstaEnLosLimitesDelMapa(nCoordX, nCoordY))
				objNodosAux[nX++] = new Id_Posicion(objNodos[nCont].nId, nCoordX, nCoordY);
		}

		// Muestra las capas. Invertimos el orden para que el movimiento al resaltar la ruta
		// sea el correcto para el caso de que el nodo de origen de la línea esté en un sector
		// distinto al activo.
		for(nX = objNodosAux.length - 1; nX >= 0; nX--)
		{
			muestraCapa(objNodosAux[nX].nId, objNodosAux[nX].nPosX, objNodosAux[nX].nPosY);
			objCapasEnSectorActivo[nContCapasSectorActivo++] = objNodosAux[nX].nId;
		}
	}
}

// Cambia el icono de una entidad según la operación que estemos realizando.
function cambiaImagen(nNodoId, nOperacion)
{
	var sRuta, sNombre; 				// Nombre y ruta de la imagen seleccionada.
	var bSeleccionado = false; 	// Indica si se ha cambiado a imagen seleccionada o deseleccionada.
	var src;										// Imagen a la que se va a cambiar.
	var nPos;										// Posición de un elemento dentro del array de nodos.

	// Obtiene la ruta completa de la imagen y extrae el nombre de la misma.
	sRuta = document.images["img" + nNodoId].src;
	sNombre = sRuta.substring(sRuta.lastIndexOf("/") + 1, sRuta.length);

	// Según la operación actual.
	switch(nOperacion)
	{
		case BORRAR_PARADAS: 
		{
			if (sNombre == IMG_PARADA_SELECCIONADA)
			{
				nPos = obtenPosicion(objNodos, nNodoId);
				if (objNodos[nPos].bFueraDeServicio == 0)
					src = IMG_PARADA_DESELECCIONADA;
				else
					src = IMG_PARADA_FUERA_DE_SERVICIO;
			}
			else
			{
				src = IMG_PARADA_SELECCIONADA;
				bSeleccionado = true;
			}
		}break;

		case MOVER_PARADAS: src = IMG_PARADA_MOVIMIENTO;break;

		case INSERTAR_PARADAS:
		{
			nPos = obtenPosicion(objNodos, nNodoId);
			if (objNodos[nPos].bFueraDeServicio == 0)
			 src = IMG_PARADA_DESELECCIONADA;
			else
				src = IMG_PARADA_FUERA_DE_SERVICIO;
		}break;

		case BORRAR_NODOS:
		{
			if (sNombre == IMG_NODO_SELECCIONADO)
				 src = IMG_NODO_DESELECCIONADO;
			else
			{
				src = IMG_NODO_SELECCIONADO;
				bSeleccionado = true;
			}
		}break;

		case MOVER_NODOS: src = IMG_NODO_MOVIMIENTO;break;
		case INSERTAR_NODOS: src = IMG_NODO_DESELECCIONADO;break;

		case INSERTAR_RUTAS:
		{
			// Si es un nodo.
			if(objNodos[obtenPosicion(objNodos, nNodoId)].nTipo == 2)
				src = IMG_NODO_SELECCIONADO;
				
			// Si es una parada.
			else
				src = IMG_PARADA_SELECCIONADA;
		}
	}

	// Actualiza la imagen y devuelve la selección.
	document.images["img" + nNodoId].src = "imagenes/nodos/" + src;
	return(bSeleccionado);
}

// -----------------------------------------------------------------------------
// Obtiene la posición dentro del array del nodo cuyo id se pasa como parámetro.
// -----------------------------------------------------------------------------
function obtenPosicion(obj, nId)
{
	var bEncontrado = false;
	var nIdRetorno = -1;
	
	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if (obj[nCont].nId == nId)
		{
			bEncontrado = true;
			nIdRetorno = nCont;
		}
	}

	return(nIdRetorno);
}

// -----------------------------------------------------------------------------------------------
// Obtiene la posición dentro del array del elemento corresponda al sector pasado como parámetro.
// -----------------------------------------------------------------------------------------------
function obtenPosicionSector(obj, nSector)
{
	var bEncontrado = false;
	var nSectorRetorno = -1;

	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if (obj[nCont] == nSector)
		{
			bEncontrado = true;
			nSectorRetorno = nCont;
		}
	}

	return(nSectorRetorno);
}

// -----------------------------------------------------------------------------
// Crea las capas precisas para visualizar paradas en la visualización de rutas.
// -----------------------------------------------------------------------------
function creaCapasParadas(objParadas)
{
	var nCont; // Contador para bucle.
	var src;

  // Crea las capas.
	for (nCont = 0; nCont < objParadas.length; nCont++)
	{
		// Declara la capa.
		document.write("<div id=cp" + objParadas[nCont].nId + " style='visibility:hidden;position:absolute;border:none;left:0;top:0' onDblClick='editaDatosParada(" + objParadas[nCont].nId + ", " + VER_RUTAS + ");'");

		if (objParadas[nCont].bFueraDeServicio)
			src = "src='imagenes/nodos/" + IMG_PARADA_FUERA_DE_SERVICIO + "' width='12' height='12'>";
		else
			src = "src='imagenes/nodos/" + IMG_PARADA_DESELECCIONADA + "' width='12' height='12'>";

		document.write("><img id = 'img" + objParadas[nCont].nId + "' " + src + "</div>");
	} // Crea las capas.

	return(objParadas);
}

// -----------------------------------------------------------------------------------------
// Muestra las paradas correspondientes al sector seleccionado en la visualización de rutas.
// -----------------------------------------------------------------------------------------
function muestraParadas(objParadas, nSector)
{
	// Recorre los nodos.
	for (var nCont = 0; nCont < objParadas.length; nCont++)
	{
		// Si el nodo pertenece al sector pasado como parámetro. Lo muestra y lo trae a primer plano.
		if (objParadas[nCont].nSector == nSector)
			muestraCapa(objParadas[nCont].nId, objParadas[nCont].nPosX, objParadas[nCont].nPosY);

		// Si no pertenece al sector.
		else
		{
			// Lo oculta.
			ocultaCapa(objParadas[nCont].nId);
		}
	}
}

// -----------------------------------------------------------------------------------------
// Devuelve la longitud de la diagonal entre dos puntos.
// -----------------------------------------------------------------------------------------
function nObtenDiagonal(nX1, nY1, nX2, nY2)
{
	return(Math.ceil(Math.sqrt(Math.abs(Math.pow((nX1 - nX2),2) + Math.pow((nY1 - nY2),2)))));
}

// Sombrea los sectores por los que pasa la ruta.
function marcaRecorridoMapaSectorial(obj, nModuloActual)
{
	var objAux = new Array();
	var nCont;
	
	// Obtiene los sectores distintos por los que pasa la ruta.
	var x = 0;
	for(nCont = 0; nCont < obj.length; nCont++)
	{
		// Si no es un nodo de relleno.
		if(obj[nCont].nTipo != 3)
		{
			// Si el sector no está en el vector auxiliar lo inserta.
			if(estaElSector(objAux, obj[nCont].nSector) == -1)
				objAux[x++] = obj[nCont].nSector;
		}
	}

	// Crea tantas capas de sombreado como sectores distintos abarque la ruta.
	for(nCont = 0; nCont < objAux.length; nCont++)
	{
		var nPosX = obtenCoordenadaMapaSectorialX(objAux[nCont], nModuloActual);
		var nPosY = obtenCoordenadaMapaSectorialY(objAux[nCont], nModuloActual);

		document.write("<div id='cpSombreado'" + objAux[nCont] + " style='position:absolute;border:none;left:" + nPosX + ";top:" + nPosY+ "'>");
		document.write("	<img id='img_sector_sombreado'" + objAux[nCont] + " src='imagenes/mapas/sector_sombreado.gif' onClick='cambiaSector(" + objAux[nCont] + "," + nModuloActual + ");' onMouseOver=cambiaPuntero('hand') onMouseOut=cambiaPuntero('default')>");
		document.write("</div>");
	}
	
	return(objAux);
}

// Indica si en el array de nodos hay alguno que pertenezca al
// pasado como parámetro.
function estaElSector(obj, nSector)
{
	var bEncontrado = false;
	var nIndice = -1;

	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if(obj[nCont] == nSector)
		{
			bEncontrado = true;
			nIndice = nCont;
		}
	}

	return(nIndice);
}

// Devuelve un vector con las rutas que pasan por la parada pasada como parámetro.
// LAS IDAS Y LAS VUELTAS SE TRATAN COMO RUTAS INDEPENDIENTES.
function obtenRutasPorParada(obj, nParadaId)
{
	var nCont2 = 0;
	var objRutas = new Array();

	// Recorremos todos los nodos existentes.
	for(var nCont = 0; nCont < obj.length; nCont++)
	{
		// Si corresponde a la parada origen.
		if(obj[nCont].nId == nParadaId)
			objRutas[nCont2++] = obj[nCont];
	}

	// Valor de retorno.
	return(objRutas);
}

// Obtiene un vector con los índices de las rutas comunes entre dos paradas.
// LAS IDAS Y LAS VUELTAS SE TRATAN COMO LA MISMA RUTA.
function obtenRutasComunes1(objOrigen, objDestino)
{
	var nCont3 = 0;
	var objRutas = new Array();

	for(var nCont=0; nCont < objOrigen.length; nCont++)
	{
		for(var nCont2=0; nCont2 < objDestino.length; nCont2++)
		{
			if(objOrigen[nCont].nRutaId == objDestino[nCont2].nRutaId || objOrigen[nCont].nRutaId == objDestino[nCont2].nVueltaId)
			{
				if(obtenPosicionRuta(objRutas, objOrigen[nCont].nRutaId) == -1)
					objRutas[nCont3++] = objOrigen[nCont];
			}
		}
	}

	// Valor de retorno.
	return(objRutas);
}

// Obtiene un vector con los índices de las rutas comunes entre dos paradas.
// LAS IDAS Y LAS VUELTAS SE TRATAN COMO RUTAS DISTINTAS.
function obtenRutasComunes2(objOrigen, objDestino)
{
	var nCont3 = 0;
	var objRutas = new Array();

	for(var nCont=0; nCont < objOrigen.length; nCont++)
	{
		for(var nCont2=0; nCont2 < objDestino.length; nCont2++)
		{
			if(objOrigen[nCont].nRutaId == objDestino[nCont2].nRutaId)
			{
				if(obtenPosicionRuta(objRutas, objOrigen[nCont].nRutaId) == -1)
					objRutas[nCont3++] = objOrigen[nCont];
			}
		}
	}

	// Valor de retorno.
	return(objRutas);
}

// -----------------------------------------------------------------------------
// Obtiene la posición dentro del array del nodo cuyo id se pasa como parámetro.
// -----------------------------------------------------------------------------
function obtenPosicionParadaRuta(obj, nId, nRutaId)
{
	var bEncontrado = false;
	var nIdRetorno = -1;

	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if (obj[nCont].nId == nId && obj[nCont].nRutaId == nRutaId)
		{
			bEncontrado = true;
			nIdRetorno = nCont;
		}
	}

	return(nIdRetorno);
}

// Obtiene la posición primera parada de la ruta pasada como parámetro.
function obtenPosicionPrimeraParada(obj, nRutaId)
{
	var bEncontrado = false;
	var nIdRetorno = -1;
	
	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if (obj[nCont].nRutaId == nRutaId)
		{
			bEncontrado = true;
			nIdRetorno = nCont;
		}
	}

	return(nIdRetorno);
}

// Obtiene la posición última parada de la ruta pasada como parámetro.
function obtenPosicionUltimaParada(obj, nRutaId)
{
	var bEncontrado = false;
	var nIdRetorno = -1;

	for(var nCont = obj.length - 1; nCont >= 0 && !bEncontrado; nCont--)
	{
		if (obj[nCont].nRutaId == nRutaId)
		{
			bEncontrado = true;
			nIdRetorno = nCont;
		}
	}

	return(nIdRetorno);
}

// Obtiene la siguiente parada a una dada dentro de la misma ruta.
function obtenSiguienteParadaRuta(obj, nPosParada)
{
	var bEncontrado = false;
	var nIndice = -1;

	for(var nCont = nPosParada + 1; nCont < obj.length && !bEncontrado && obj[nCont].nRutaId == obj[nPosParada].nRutaId; nCont++)
	{
		if (obj[nCont].nTipo == 1)
		{
			bEncontrado = true;
			nIndice = nCont;
		}
	}

	return(nIndice);
}

// -------------------------------------------------------------------------------------
// Obtiene la posición dentro del array del nodo cuyo id de ruta se pasa como parámetro.
// -------------------------------------------------------------------------------------
function obtenPosicionRuta(obj, nRutaId)
{
	var bEncontrado = false;
	var nIdRetorno = -1;

	for(var nCont = 0; nCont < obj.length && !bEncontrado; nCont++)
	{
		if (obj[nCont].nRutaId == nRutaId)
		{
			bEncontrado = true;
			nIdRetorno = nCont;
		}
	}

	return(nIdRetorno);
}

// Ordena los transbordos por ruta de llegada y en caso de coincidencia, por ruta de salida.
function ordenaTransbordos(objParadas)
{
	for(var x = 0; x < objParadas.length - 1; x++)
	{
		for(var y = x + 1; y < objParadas.length; y++)
		{
			// Se ordenan por ruta de llegada.
			if(objParadas[x].nRutaLlegada > objParadas[y].nRutaLlegada)
			{
				auxNodoTransbordo = objParadas[x];
				objParadas[x] = objParadas[y];
				objParadas[y] = auxNodoTransbordo;
			}

			// Si las rutas de llegada coinciden, se ordenan por ruta de salida
			else if(objParadas[x].nRutaLlegada == objParadas[y].nRutaLlegada)
			{
				if(objParadas[x].nRutaSalida > objParadas[y].nRutaSalida)
				{
					auxNodoTransbordo = objParadas[x];
					objParadas[x] = objParadas[y];
					objParadas[y] = auxNodoTransbordo;
				}
			}
		}
	}

	// Valor de retorno.
	return(objParadas);
}

// ---------------------------------------------------------------------
// PARA OBTENER PORCIONES DE RUTA
// ---------------------------------------------------------------------

// Obtiene una ruta completa.
function obtenRutaCompleta(obj, nRutaId)
{
	var objRuta = new Array();

	var nOrigen = obtenPosicionPrimeraParada(obj, nRutaId);
	var nFin = obtenPosicionUltimaParada(obj, nRutaId);
	objRuta = obj.slice(nOrigen, nFin + 1);
	return(objRuta);
}

// Obtiene el recorrido que sigue una ruta para llegar a una posición determinada.
function obtenRutaAnteriorAId(obj, nRutaId, nId)
{
	var objRuta = new Array();

	var nOrigen = obtenPosicionPrimeraParada(obj, nRutaId);
	var nFin = obtenPosicionParadaRuta(obj, nId, nRutaId);
	objRuta = obj.slice(nOrigen, nFin + 1);
	return(objRuta);
}

// Obtiene el recorrido que se sigue desde una parada hasta final de trayecto.
function obtenRutaPosteriorAId(obj, nRutaId, nId)
{
	var objRuta = new Array();

	var nOrigen = obtenPosicionParadaRuta(obj, nId, nRutaId);
	var nFin = obtenPosicionUltimaParada(obj, nRutaId);
	objRuta = obj.slice(nOrigen, nFin + 1);
	return(objRuta);
}

// Obtiene el recorrido que se sigue desde una parada hasta final de trayecto.
function obtenRutaEntreIds(obj, nRutaId, nIdOrigen, nIdDestino)
{
	var objRuta = new Array();

	var nOrigen = obtenPosicionParadaRuta(obj, nIdOrigen, nRutaId);
	var nFin = obtenPosicionParadaRuta(obj, nIdDestino, nRutaId);

	// Si el origen es anterior al fin, devuelve la porción hasta el final de la ruta.
	if(nOrigen > nFin)
		nFin = obtenPosicionUltimaParada(obj, nRutaId)

	objRuta = obj.slice(nOrigen, nFin + 1);
	return(objRuta);
}