/* Auto-Bus Widget City File For Portland Oregon USA
 * Version 1.0.0
 * March 12th 2007
 * 
 * Copyright 2007 Frederic Weigand Warr
 *
 * This file is an addition to the Auto-Bus widget for the Yahoo! Widget Engine.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
//-----------------------------------------------------------------------
/*
 * Adding and Editing Bus Stops for Portland, OR
 * 
 * Simply enter your bus line number and stop ID.
 * You may optionally enter any text as direction and stop information
 * to be displayed on the widget and to distiguish the stop in the
 * edit stops dialog.
 * Note that as of version 1.2.0 and earlier of the Auto-Bus widget,
 * not giving any display information will make stops on the same
 * line difficult to distinguish in the Edit Stops dialog.
 * Rather than not specifying the display text, I would suggest
 * disabling their display in the widget's preferences.
 */

//-----------------------------------------------------------------------

/* Note about city specific js files
 * 
 * This file contains code specific to Portland's TriMet transit system.
 * To make a city file for another transit system, use this as a template
 * and follow the instructions given in the comments above each function.
 * There are 7 functions that are necessary to implement to create a city
 * file. These are: getData, getTimes, getLineStr, getStpStr, getDirStr,
 * addStop and editStop. The first 5 (the getters) are methods of the
 * BusStop object and can therefore access the particular bus stop's info
 * through the keyword this. The last 2, are global functions to allow
 * the user to add STOPS and edit the STOPS list. Since these are global
 * functions, access to the bus STOPS is done through the "STOPS" array.
 * 
 * If you do create a functional city file for another transit system,
 * please send it to fwwarr@hotmail.com so that it can be posted on the
 * Auto-Bus widget's web page and be accessible for others to use.
 */

//------------------------------------------------------------------------
/* BusStop object methods
 */


/* method getData(doGetInfo)
 * 
 * Retrieves a bus stop's data from the web.
 * 
 * This is a method of the BusStop object
 * so the corresponding BusStop can be accessed
 * using "this" in the method body.
 * 
 * param doGetInfo
 * 
 *          If true, this method will call all of
 *          getTimes, getLineStr, getStpStr and getDirStr
 *          once the data has been received and appropriately
 *          refresh the display for all this information.
 *          
 *          If false, this method will only call getTimes
 *          once the data has been received and only the
 *          display of the times will be refreshed.
 * 
 * Note: Typically to adapt this method to a particular transit system
 * only the url location would have to be modified. Although this
 * may not be the case for every transit system.
 */
BusStop.prototype.getData = function (doGetInfo) {
	
	var url = new URL();
	var now,
		urlString;
	// Do not modify above this line
	//------------------------------
	// Start of city specific code
	
	url.location = "http://www.trimet.org/arrivals/tracker.html?locationID=" + this.args[1];
	
	// End of city specific code
	//-----------------------------
	//Do not modify below this line
	url.object = this;
	appendLineToFile(LOGPATH, "Fetching data for " + this.args[0]);
	if (doGetInfo) {
		url.fetchAsync(useData);
	} else {
		url.fetchAsync(useTimeData);
	}
}


/* method getTimes()
 * 
 * Extracts the next few passage times from the data fetched by the getData method.
 * That data is accessible to this method through "this.data".
 * 
 * This method is expected to return an array of Date objects.
 * 
 * This method is called after each bus passage so that the first element
 * of the array is always the very next passage time.
 * 
 * If there is no way of getting the next passage times (eg. there are no more passages today),
 * this method should return an empty array and appropriately modify this.nextUpdateTime
 * with a Date object representing the next time the widget should try to fetch the times.
 * 
 * The entirety of this method's code is city specific
 */
BusStop.prototype.getTimes = function () {
	var i,
		j,
		str,
		tstr,
		time,
		passageTimes;

	passageTimes = [];
	str = this.data.substring(this.data.indexOf("table") + 5);
	str = str.substring(0, str.indexOf("table"));
	j=0;
	for(i=0;i<10;i++){
		str = str.substring(str.indexOf("<td colspan")+65-this.args[0].length);
		if(str.substring(0,str.indexOf(" ")) == this.args[0]) {
			tstr = str.substring(str.indexOf("<div id='a" + i + "'>")+13);
			tstr = tstr.substring(0, tstr.indexOf("<"));
			time = convertToDate(tstr);
			if( time != ""){
				passageTimes[j] = time;
				j++;
			}
		}
	}
	
	return passageTimes;
}

/* method getLineStr()
 * 
 * This method returns the string to display as the bus' line.
 * Typically this information is already stored in the bus stop's
 * args array at index 0, but if different information is required
 * from the web, the data accessed by getData is still accessible
 * through this.data.
 */
BusStop.prototype.getLineStr = function () {
	return this.args[0];
}

/* method getStpStr()
 * 
 * This method returns the string to display as the bus stop info.
 * 
 * Depending on the ciy, this string may be specified by the user 
 * and stored in the bus stop's args array at index 1, but typically
 * the info stored in args[1] is not an easily recognizable string
 * (i.e. it's just a stop number) and the string to display must be
 * extracted from the web data.
 * 
 * Alternatively, the user can be asked to specify this
 * string (as well as a bus stop number if necessary) upon
 * creation/modification of the bus stop (by methods addStop
 * and editStop) and stored in this.args[7]. In that case
 * this method would only have to return this.args[7].
 */
BusStop.prototype.getStpStr = function () {
	return this.args[7];
}

/* method getDirStr()
 * 
 * This method returns the string to display as the direction
 * for which the times should be displayed.
 * 
 * Depending on the city, this string could be specified directly 
 * by the user and stored in args[2]. However, for some bus systems,
 * direction information is not needed to completely specify a bus stop
 * and the direction can be found on the web without the user specifying
 * it at all. (For example, in Montreal, a bus stop is fully qualified 
 * by a route number and a stop number and the direction corresponding
 * to that bus stop can be taken from the web.)
 * 
 * This method should not actually fetch data from the web but rather
 * use data fetched by getData and stored in this.data.
 */
BusStop.prototype.getDirStr = function () {
	return this.args[8];
}

//------------------------------------------------------------------------
/* Global functions
 */


/* function addStop()
 * 
 * Adds a bus stop to the array of STOPS to monitor.
 * Should create a form for the user to input the
 * necessary properties about the bus stop. A new
 * BusStop object created with the given properties
 * should then be added to the end of the STOPS array.
 * All other updating and displaying is taken care of
 * by non city specific code.
 * Typically the form should contain fields to specify
 * the route number, the stop name or number and sometimes
 * also the direction. These 3 properties should be stored
 * respectively in indexes 0, 1 and 2 of the  array.
 * This means that the BusStop object can be created simply
 * using the constructor BusStop(args) where args is an
 * array containing the three mentionned properties in
 * indexes 0 through 2.
 */
function addStop() {
	
	var formFields = new Array();
	var i = 0;
	formFields[i] = new FormField();
	formFields[i].title = "Line Number";
	formFields[i].type = "text";
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Stop ID";
	formFields[i].type = "text";
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Stop Display Text";
	formFields[i].type = "text";
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Direction Display Text";
	formFields[i].type = "text";
		
	var formResults = form( formFields, "Add a bus stop", "OK" );
	
	if( formResults != null ){
		
		var args = [];
		args[0] = formResults[0];
		args[1] = formResults[1];
		args[7] = formResults[2];
		args[8] = formResults[3];
		STOPS[STOPS.length] = new BusStop(args);
		
		// End of city specific code
		//------------------------------
		// Do not modify below this line
		STOPS[STOPS.length - 1].initDisplay();
		STOPS[STOPS.length - 1].getData(true);
		makeDisplay();
		saveStops();
	}
}

/* function editStop()
 * 
 * Provides the form for editing the basic properties of a bus stop.
 * Properties that define a particular bus stop should be specified
 * by the user in this form and saved in the BusStop object's args
 * array in indexes 0 through 2.
 * 
 * param busStop
 * 
 *           Indicates the indes of the STOPS array corresponding
 *           to the bus stop to be modified. The BusStop object to
 *           modify is therefore available through STOPS[busStop].
 * 
 * Typically, index 0 corresponds to the route number,
 * index 1 corresponds to the stop name or number, and
 * index 2 corresponds to the direction.
 * If additional information is needed to fully describe a bus stop,
 * indexes greater than 8 could be used.
 */
function editStop(busStop){
	
	var formFields = new Array();
	var i = 0;
	formFields[i] = new FormField();
	formFields[i].title = "Line Number";
	formFields[i].type = "text";
	formFields[i].value = STOPS[busStop].args[0].substring(3);
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Stop ID";
	formFields[i].type = "text";
	formFields[i].value = STOPS[busStop].args[1];
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Stop Display Text";
	formFields[i].type = "text";
	formFields[i].value = STOPS[busStop].args[7];
	i++;
	formFields[i] = new FormField();
	formFields[i].title = "Direction Display Text";
	formFields[i].type = "text";
	formFields[i].value = STOPS[busStop].args[8];
	
	var formResults = form( formFields, "Edit bus stop", "OK" );
	
	if( formResults != null ){
		var args = [];
		STOPS[busStop].args[0] = formResults[0];
		STOPS[busStop].args[1] = formResults[1];
		STOPS[busStop].args[7] = formResults[2];
		STOPS[busStop].args[8] = formResults[3];		
		// End of city specific code
		//------------------------------
		// Do not modify below this line
		STOPS[busStop].getData();
		makeDisplay();
		saveStops();
	}
}



//--------------------------------------------------------------------------
/* Utility functions
 * 
 * Any other functions that may be used by the 7 previous functions but that
 * are not necessary to implement for every transit system should go here.
 */

// Converts the string from the Transit tracker into a Date object
// Supports these formats:
// int min
// hh:mm
// hh:mm p.m
// Due
function convertToDate(str){
	var now,
		hr,
		min;

	now = new Date();
	if(str.indexOf("min") > 0) {
		min = str.substring(0,str.indexOf("min")-1);
		now.setTime(now.getTime() + min*60*1000);
		return now;
	}
	if(str.indexOf(":") > 0) {
		min = parseInt(str.substr(str.indexOf(":")+1, 2));
		hr = parseInt(str.substr(str.indexOf(":")-2, 2));
		if(str.indexOf("p.m") > 0 && hr < 12) {
			hr = hr + 12;
		}
		now.setHours(hr);
		now.setMinutes(min);
		return now;
	}
	if(str.indexOf("Due") > -1) {
		return now;
	}
	return "";
}
