var ja = {
	version: '0.1',
	onLoad: function (f) { Event.observe(window, 'load', f) },
	nop: function () { },
	k: function (x) { return function () { return x } },
	i: function(x) { return x }
}


// extensions: Number
Number.prototype.div = function(d) { return Math.floor(this/d) }

// extensions: String
String.prototype.toTitleCase = function () { return this.split(/\s/).invoke('capitalize').join(' ') }

// extensions: Date
Date.today = function () {
	var d = new Date()
	d.setHours(0,0,0,0)
	return d
}
Date.easter = function (y) {
	// Gregorian western easter sunday
	// accurate from 325 to 4099 A.D.
	// based on http://www.gmarts.org/index.php?go=415#easterscript
	if (y < 325 || y > 4099) return null
	var c = y.div(100)
	var h = (c - c.div(4) - (8 * c + 13).div(25) + 19 * (y % 19) + 15) % 30
	var i = h - h.div(28) * (1 - h.div(28) * (29).div(h + 1) * (21).div(11))
	return new Date(y, 2, i - ((y + y.div(4) + i + 2 - c + c.div(4)) % 7) + 28)
}
Date.prototype.clone = function() { return new Date(this.getTime()) }
Date.prototype.sameDay = function (d) { return this.getTime().div(86400000) == d.getTime().div(86400000) }
Date.prototype.nextDay = function(amount) {
	if (Object.isUndefined(amount)) amount = 1
	var r = this.clone()
	r.setDate(r.getDate() + amount)
	return r
}
Date.prototype.prevDay = function(amount) { return this.nextDay(Object.isUndefined(amount) ? -1 : -amount) }
Date.prototype.nextMonth = function(amount) {
	if (Object.isUndefined(amount)) amount = 1
	var r = this.clone()
	r.setMonth(r.getMonth() + amount)
	return r
}
Date.prototype.prevMonth = function(amount) { return this.nextMonth(Object.isUndefined(amount) ? -1 : -amount) }

Date.prototype.format = (function(){
	var s = { // symbols
		// fractions
		f:    function(d,l) { return d.getMilliseconds().div(100) },
		ff:   function(d,l) { return d.getMilliseconds().div(10).toPaddedString(2) },
		fff:  function(d,l) { return d.getMilliseconds().toPaddedString(3) },
		// seconds
		s:    function(d,l) { return d.getSeconds() },
		ss:   function(d,l) { return this.s(d,l).toPaddedString(2) },
		// primes
		p:    function(d,l) { return d.getMinutes() },
		pp:   function(d,l) { return this.m(d,l).toPaddedString(2) },
		// hours (12)
		h:    function(d,l) { return d.getHours() < 13 ? d.getHours() : d.getHours() - 12 },
		hh:   function(d,l) { return this.h(d,l).toPaddedString(2) },
		// hours (24)
		H:    function(d,l) { return d.getHours() },
		HH:   function(d,l) { return this.H(d,l).toPaddedString(2) },
		// am/pm
		tt:   function(d,l) { return l.ampm[d.getHours() < 12 ? 0 : 1] },
		Tt:   function(d,l) { return this.tt(d,l).toTitleCase() },
		TT:   function(d,l) { return this.tt(d,l).toUpperCase() },
		// week days
		w:    function(d,l) { return l.days1[d.getDay()] },
		www:  function(d,l) { return l.days3[d.getDay()] },
		wwww: function(d,l) { return l.days[d.getDay()] },
		W:    function(d,l) { return this.w(d,l).toUpperCase() },
		WW:   function(d,l) { return this.ww(d,l).toUpperCase() },
		WWW:  function(d,l) { return this.www(d,l).toUpperCase() },
		Ww:   function(d,l) { return this.ww(d,l).toTitleCase() },
		Www:  function(d,l) { return this.www(d,l).toTitleCase() },
		// days
		d:    function(d,l) { return d.getDate() },
		dd:   function(d,l) { return d.getDate().toPaddedString(2) },
		// months
		m:    function(d,l) { return d.getMonth() +1 },
		mm:   function(d,l) { return this.m(d,l).toPaddedString(2) },
		// month names
		mmm:  function(d,l) { return l.months3[d.getMonth()] },
		mmmm: function(d,l) { return l.months[d.getMonth()] },
		MMM:  function(d,l) { return this.mmm(d,l).toUpperCase() },
		MMMM: function(d,l) { return this.mmmm(d,l).toUpperCase() },
		Mmm:  function(d,l) { return this.mmm(d,l).toTitleCase() },
		Mmmm: function(d,l) { return this.mmmm(d,l).toTitleCase() },
		// years
		yy:   function(d,l) { return d.getFullYear().toString().substring(2, 4) },
		yyyy: function(d,l) { return d.getFullYear() }
	}
	var r = new RegExp('ff?f?|ss?|pp?|hh?|HH?|tt|Tt|TT|ww?w?|WW?W?|Ww?w?|dd?|mm?m?m?|MMMM?|Mmmm?|yy(?:yy)?','g')
	var f = function(l,d,t) { return s[t] ? s[t](d,l) : t }
	return function (d,p,l) { // (date,pattern,locale)
		var loc = ja.i18n.getLocale(l||'c')
		return (p||loc.formats.date).replace(r,f.curry(loc,d))
	}.methodize()
})()

;(function(){
	Date.nativeParse = Date.parse
	var extendedParse = function(src,p) {
		src = String(src)
		if (src.strip() == '') return new Date(NaN)
		var st = {
			s: new String(src),
			p: 0,
			r: Date.today()
		}
		var digits = function(st,min,max) {
			max = max || min
			var x = st.s.substr(0,max).replace(/[^\d]*$/,'')
			st.s = st.s.substr(x.length)
			return x.length < min ? NaN : new Number(x)
		}
		var s = {
			d: function(st) {
				var f = digits(st,1,2)
				st.r.setDate(f)
			},
			m: function(st) {
				var f = digits(st,1,2)
				st.r.setMonth(f-1)
			},
			y: function(st) {
				var f = digits(st,1,4)
				if (f <  100) f += new Date().getFullYear().div(100) * 100
				st.r.setFullYear(f)
			}
		}
		//var r = new RegExp('ff?f?|ss?|pp?|hh?|HH?|tt|Tt|TT|ww?w?|WW?W?|Ww?w?|dd?|mm?m?m?|MMMM?|Mmmm?|yy(?:yy)?|.','g')
		var r = new RegExp('d|m|y|.','g')
		var f = function(st,t) {
			if (isNaN(st.r)) return ''
			if (s[t]) {
				s[t](st)
			} else {
				if (st.s.charAt(0) == t)  st.s = st.s.substr(1)
				else {
					//console.error('cant\'parse',st.s)
					st.r = new Date(NaN)
				}
			}
			return ''
		}
		p.replace(r,f.curry(st))
		return st.r
	}
	Date.parse = function (s) { return (arguments.length == 2 ? extendedParse : Date.nativeParse).apply(this,arguments) }
})()

