/*
DateFormat.js: formats JavaScript Dates.
Copyright (C) 1999  Jan Wessely 

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Version 2 as published by the Free Software Foundation.

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
or browse to http://www.gnu.org/copyleft/gpl.html.

Created: 27 Dec 1999
Last modified: 25 Sep 2000
*/

// constructors ***************************************************************

// DateFormat()
// DateFormat(string pattern, opt Locale locale)
// DateFormat(number dateStyle, opt number timeStyle, opt string locale)

function DateFormat()
{
	var argv = DateFormat.arguments;
	var argc = argv.length;

	if(argc == 0)
	{
		this.pattern = "";
		this.locale = Locale.availableLocales[Locale.DEFAULT];
	}
	else if(typeof argv[0] == "string")
	{
		this.pattern = argv[0];
		this.locale = (argc >= 2) ? argv[1] : Locale.availableLocales[Locale.DEFAULT];
	}
	else
	{
		this.locale = argc >= 3 && argv[2] ? argv[2] : Locale.availableLocales[Locale.DEFAULT];
		this.setStyle(argv[0], argc > 1 ? argv[1] : DateFormat.NONE);
	}
}

// methods ********************************************************************

function /*string*/ _DateFormat_format(/*Date or string*/ date)
{
	//sanity check
	if(!date)
		return "";
	if(typeof date == "string")
		date = new Date(date);

	var s = "";
	var pos = 0;
	while(pos < this.pattern.length)
	{
		var c = this.pattern.charAt(pos);
		var length;
		var symbol = DateFormat.SYMBOLS[c];

		if(symbol)
		{
			length = this._consume(pos, c);
			s += symbol.format(date, length, this.locale);
		}
		else if(c == "'")
		{
			if(pos >= this.pattern.length - 1)	// last char
			{
				length = 1;
				s += c;
			}
			else if(this.pattern.charAt(pos + 1) != "'")	// delim, not escape
			{
				var eos = this.pattern.indexOf("'", ++pos);
				if(eos == -1) eos = this.pattern.length;	// don't be anal
				length = (eos - pos) + 1;
				s += this.pattern.substring(pos, eos);
			}
			else	// escape
			{
				length = 2;
				s += c;
			}
		}
		else	// treat any other char as literal
		{
			length = 1;
			s += c;
		}

		pos += length;
	} // while

	return s;
}

function _DateFormat_setStyle(/*number*/ dateStyle, /*number*/ timeStyle)
{
	dateStyle = parseInt(dateStyle);
	timeStyle = parseInt(timeStyle);

	this.pattern = this.locale.datePattern[dateStyle];
	if(this.pattern && timeStyle)
		this.pattern += " ";
	this.pattern += this.locale.timePattern[timeStyle];
}

// private methods ************************************************************

// returns number of same chars as c in this.pattern starting with pos
function _DateFormat_consume(pos, c)
{
	var i = pos;
	while(++i < this.pattern.length)
		if(this.pattern.charAt(i) != c)
			break;
	return i - pos;
}

// pads a number with leading zeros so that it has at least l digits
function _DateFormat_formatNumber(n, l, locale)
{
	var s = "";
	var digits = (new String(n)).length;
	var zeros = Math.max(0, l - digits);
	for(var i = 0; i < zeros; i++)
		s += "0";
	return s + n;
}

function _DateFormat_formatEra(year, l, locale)
{
	var era = year < 0 ? DateFormat.BC : DateFormat.AD;
	return locale.era[era];
}

function _DateFormat_formatMonth(month, l, locale)
{
	if(l < 3)
		return DateFormat._formatNumber(month + 1, l, locale);
	else
	{
		var months = l == 3 ? locale.shortMonths : locale.longMonths;
		return months[month];
	}
}

function _DateFormat_formatWeekday(weekday, l, locale)
{
	var weekdays = l < 4 ? locale.shortWeekdays : locale.longWeekdays;
	return weekdays[weekday];
}

function _DateFormat_formatAMPM(hour, l, locale)
{
	var ampm = hour <= 12 && hour > 0 ? DateFormat.AM : DateFormat.PM;
	return locale.amPm[ampm];
}

function _DateFormat_formatTimezone(offset, l, locale)
{
	// todo localized Timezone names
	var diff = Math.abs(offset);
	var hours = DateFormat._formatNumber(parseInt(diff / 60), 2);
	var minutes = DateFormat._formatNumber(diff % 60, 2);
	return "GMT" + (offset > 0 ? "-" : "+") + hours + ":" + minutes;
}

function _DateFormat_formatMillis(date, l, locale)
{
	var date2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
	return DateFormat._formatNumber(date.getTime() - date2.getTime(), l, locale);
}

function _DateFormat_formatYear(date, l, locale)
{
    var year = date.getYear();
    var fullYear = date.getFullYear();
    if(year != fullYear && year == 100)
        year = 0;
	var y = l > 2 ? fullYear : year;
	return DateFormat._formatNumber(y, l, locale);
}

DateFormat.NONE = 0;
DateFormat.SHORT = 1;
DateFormat.MEDIUM = 2;
DateFormat.LONG = 3;
DateFormat.FULL = 4;
DateFormat.NSTYLES = 5;

DateFormat.BC = 0;
DateFormat.AD = 1;

DateFormat.AM = 0;
DateFormat.PM = 1;

DateFormat._formatNumber = _DateFormat_formatNumber;
DateFormat._formatEra = _DateFormat_formatEra;
DateFormat._formatMonth = _DateFormat_formatMonth;
DateFormat._formatWeekday = _DateFormat_formatWeekday;
DateFormat._formatAmPm = _DateFormat_formatAMPM;
DateFormat._formatTimzone = _DateFormat_formatTimezone;
DateFormat._formatMillis = _DateFormat_formatMillis;
DateFormat._formatYear = _DateFormat_formatYear;

DateFormat.SYMBOLS = new Object();
DateFormat.SYMBOLS["G"] = new _DFSymbol(DateFormat._formatEra, "date.getFullYear()");
DateFormat.SYMBOLS["y"] = new _DFSymbol(DateFormat._formatYear, "date");
DateFormat.SYMBOLS["M"] = new _DFSymbol(DateFormat._formatMonth, "date.getMonth()");
DateFormat.SYMBOLS["d"] = new _DFSymbol(DateFormat._formatNumber, "date.getDate()");
DateFormat.SYMBOLS["h"] = new _DFSymbol(DateFormat._formatNumber, "date.getHours() > 12 ? date.getHours() - 12 : (date.getHours() > 0 ? date.getHours() : 12)");
DateFormat.SYMBOLS["H"] = new _DFSymbol(DateFormat._formatNumber, "date.getHours()");
DateFormat.SYMBOLS["m"] = new _DFSymbol(DateFormat._formatNumber, "date.getMinutes()");
DateFormat.SYMBOLS["s"] = new _DFSymbol(DateFormat._formatNumber, "date.getSeconds()");
DateFormat.SYMBOLS["S"] = new _DFSymbol(DateFormat._formatMillis, "date");
DateFormat.SYMBOLS["E"] = new _DFSymbol(DateFormat._formatWeekday, "date.getDay()");
DateFormat.SYMBOLS["D"] = new _DFSymbol();
DateFormat.SYMBOLS["F"] = new _DFSymbol();
DateFormat.SYMBOLS["w"] = new _DFSymbol();
DateFormat.SYMBOLS["W"] = new _DFSymbol();
DateFormat.SYMBOLS["a"] = new _DFSymbol(DateFormat._formatAmPm, "date.getHours()");
DateFormat.SYMBOLS["k"] = new _DFSymbol(DateFormat._formatNumber, "date.getHours() + 1");
DateFormat.SYMBOLS["K"] = new _DFSymbol(DateFormat._formatNumber, "date.getHours() > 11 ? date.getHours() - 12 : date.getHours()");
DateFormat.SYMBOLS["z"] = new _DFSymbol(DateFormat._formatTimezone, "date.getTimezoneOffset()");

DateFormat.prototype.format = _DateFormat_format;
DateFormat.prototype.setStyle = _DateFormat_setStyle;
DateFormat.prototype._consume = _DateFormat_consume;

// _DFSymbol ***********************************************************

// helper object
function _DFSymbol(func, value)
{
	this.func = func;
	this.value = value;
}

function _DFSymbol_format(date, l, locale)
{
	return this.func ? this.func(eval(this.value), l, locale) : "";
}

_DFSymbol.prototype.format = _DFSymbol_format;

// Locale *******************************************************************

function Locale(/*string*/ name, /*opt Locale or string*/ parent)
{
	this.name = name;
	Locale.availableLocales[this.name] = this;
	this.shortWeekdays = new Array("", "", "", "", "", "", "");
	this.longWeekdays = new Array("", "", "", "", "", "", "");
	this.shortMonths = new Array("", "", "", "", "", "", "", "", "", "", "", "");
	this.longMonths = new Array("", "", "", "", "", "", "", "", "", "", "", "");
	this.era = new Array("", "");
	this.amPm = new Array("", "");
	this.datePattern = new Array("", "", "", "", "");
	this.timePattern = new Array("", "", "", "", "");

	if(parent)
	{
		if(typeof parent == "string")
			parent = Locale.availableLocales[parent];
		copy(parent.shortWeekdays, this.shortWeekdays);
		copy(parent.longWeekdays, this.longWeekdays);
		copy(parent.shortMonths, this.shortMonths);
		copy(parent.longMonths, this.longMonths);
		copy(parent.era, this.era);
		copy(parent.amPm, this.amPm);
		copy(parent.datePattern, this.datePattern);
		copy(parent.timePattern, this.timePattern);
	}
}

function _Locale_toString()
{
	return this.name;
}

Locale.prototype.toString = _Locale_toString;
Locale.availableLocales = new Object();

// helper functions ***********************************************************

// shallow copy
function /*Object*/ copy(/*object*/ src, /*opt object*/ dst)
{
	if(!dst) dst = new Object();
	for(var prop in src)
		dst[prop] = src[prop];
	return dst;
}

// Locale definitions *********************************************************

var tmp;
Locale.DEFAULT = "en";

tmp = new Locale("en");
tmp.shortWeekdays = new Array("Su", "Mo", "Tu", "We", "Fr", "Sa");
tmp.longWeekdays = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Friday", "Saturday");
tmp.shortMonths = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
tmp.longMonths = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
tmp.era = new Array("BC", "AD");
tmp.amPm = new Array("AM", "PM");
tmp.datePattern = new Array("", "M/d/yy", "MMM d, yyyy", "MMMM d, yyyy", "EEEE, MMMM d, yyyy");
tmp.timePattern = new Array("", "h:mm a", "h:mm:ss a", "h:mm:ss a z", "h:mm:ss a z");

tmp = new Locale("en_GB", "en");
tmp.datePattern = new Array("", "dd/MM/yy", "dd-MMM-yyyy", "dd MMMM yyyy", "dd MMMM yyyy");
tmp.timePattern = new Array("", "HH:mm", "HH:mm:ss", "HH:mm:ss z", "HH:mm:ss o''cloc'k' z");

tmp = new Locale("de");
tmp.shortWeekdays = new Array("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa");
tmp.longWeekdays = new Array("Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag");
tmp.shortMonths = new Array("Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez");
tmp.longMonths = new Array("Januar", "Februar", "M?rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember");
tmp.era = new Array("v. Chr.", "n. Chr.");
tmp.datePattern = new Array("", "dd.MM.yy", "dd.MM.yyyy", "dd. MMMM yyyy", "d. MMMM yyyy");
tmp.timePattern = new Array("", "HH:mm", "HH:mm:ss", "HH:mm:ss z", "HH:mm:ss U'h'r z");

tmp = new Locale("de_AT", "de");
tmp.shortMonths = new Array("J?n", "Feb", "M?r", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez");
tmp.longMonths = new Array("J?nner", "Februar", "M?rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember");

tmp = new Locale("fr");
tmp.shortWeekdays = new Array("dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam.");
tmp.longWeekdays = new Array("dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi");
tmp.shortMonths = new Array("janv.", "f¨¦vr.", "mars", "avr.", "mai", "juin", "juil.", "ao?t", "sept.", "oct.", "nov.", "d¨¦c.");
tmp.longMonths = new Array("janvier", "f¨¦vrier", "mars", "avril", "mai", "juin", "juillet", "ao?t", "septembre", "octobre", "novembre", "d¨¦cembre");
tmp.era = new Array("BC", "ap. J.-C.");
tmp.datePattern = new Array("", "dd/MM/yy", "d MMM yy", "d MMMM yyyy", "EEEE d MMMM yyyy");
tmp.timePattern = new Array("", "HH:mm", "HH:mm:ss", "HH:mm:ss z", "HH 'h' mm z");

tmp = new Locale("it");
tmp.shortWeekdays = new Array("dom", "lun", "mar", "mer", "gio", "ven", "sab");
tmp.longWeekdays = new Array("domenica", "luned¨¬", "marted¨¬", "mercoled¨¬", "gioved¨¬", "venerd¨¬", "sabato");
tmp.shortMonths = new Array("gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic");
tmp.longMonths = new Array("gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre");
tmp.era = new Array("BC", "dopo Cristo");
tmp.datePattern = new Array("", "dd/MM/yy", "d-MMM-yy", "d MMMM yyyy", "EEEE d MMMM yyyy");
tmp.timePattern = new Array("", "H.mm", "H.mm.ss", "HH.mm.ss z", "HH.mm.ss z");

tmp = new Locale("es");
tmp.shortWeekdays = new Array("dom", "lun", "mar", "mi¨¦", "jue", "vie", "s¨˘b");
tmp.longWeekdays = new Array("domingo", "lunes", "martes", "mi¨¦rcoles", "jueves", "viernes", "s¨˘bado");
tmp.shortMonths = new Array("ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic");
tmp.longMonths = new Array("enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre");
tmp.era = new Array("BC", "AD");
tmp.datePattern = new Array("", "d/MM/yy", "d-MMM-yy", "d 'de' MMMM 'de' yyyy", "EEEE d 'de' MMMM 'de' yyyy");
tmp.timePattern = new Array("", "H:mm", "H:mm:ss", "HH:mm:ss z", "HH'H'mm'' z");

tmp = new Locale("pt");
tmp.shortWeekdays = new Array("Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "S¨˘b");
tmp.longWeekdays = new Array("Domingo", "Segunda-feira", "Ter?a-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "S¨˘bado");
tmp.shortMonths = new Array("Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez");
tmp.longMonths = new Array("Janeiro", "Fevereiro", "Mar?o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro");
tmp.era = new Array("BC", "AD");
tmp.datePattern = new Array("", "dd-MM-yy", "d/MMM/yy", "d 'de' MMMM 'de' yyyy", "EEEE, d 'de' MMMM 'de' yyyy");
tmp.timePattern = new Array("", "H:mm", "H:mm:ss",  "HH:mm:ss z", "HH'H'mm'm' z");


