Created
August 17, 2011 11:40
-
-
Save dotmh/1151374 to your computer and use it in GitHub Desktop.
A Prototype Calander
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Options | |
* ======= | |
* Required | |
* -------- | |
* element : a protoype element / or an DOM Id [Required] | |
* from : The date to go from can ether be a string or a offest (see below about offests and date string) | |
* to : The date to go to can ether be string of a offset (see below about offests and date string) | |
* | |
* Optional | |
* -------- | |
* field : the field that will be populated with the date [Optional] if ommited then the date param will containing the date | |
* exclude_weekends : Whether to include weekends that fall in the range given Boolean true , false | |
* exclude_dates : An array of dates to be excluded using ether an offset or date string | |
* events : An object literal containing events see below for event list | |
* format : the format of the date to be returned see below for format options | |
* local_days : Allows you to localise the day labels , should be an array [sun..sat] | |
* local_months : Allows you to localise the month labels , should be an array [jan..dec] | |
* | |
* Offest And Date string | |
* ====================== | |
* You can enter the date range as ether a offset using the following format | |
* <number><unit> | |
* where number is the offset number and unit is weeks , days or months | |
* d / D = days | |
* w / W = weeks | |
* m / M = months | |
* | |
* Date strings need to formatted as <YYYY>-<MM>-<DD> | |
* | |
* Return Format | |
* ============= | |
* This is the return format as a string with placeholders as follows | |
* d = day | |
* D = day (with leading zero) | |
* m = month | |
* M = month ( with leading zero) | |
* y = short year i.e 11 | |
* Y = Full Year i.e 2011 | |
* | |
* i.e | |
* For 1st July 2011 | |
* | |
* D/M/Y 01/07/2011 | |
* D-M-Y 01-07-2011 | |
* Y-M-D 2011-07-01 | |
* | |
* Events | |
* ====== | |
* You can bind event listners to the following events by passing an event | |
* object litral i.e event : { event_name : function(event) { some code }} | |
* The event will be in the event var. | |
* Events can be passed on the constuct | |
* | |
* *next_event : When the user clicks the next button | |
* *back_event : when the user clicks the back button | |
* *date_event : When a user clicks on a date field | |
* *inactive_date_event : When a user clicks a date thats not active | |
* | |
* There are also <event name>_before that is triggered before that event and | |
* <event name>_after that is triggered after that event. | |
* | |
* Styling | |
* Dates that can be selected are given a class of active , | |
* Dates that can not be selected are given a class of inactive | |
*/ | |
var dateCalander = Class.create({ | |
element : '', | |
field : '', | |
date : '', | |
fromDate : -1, | |
toDate : -1, | |
events : -1, | |
format : 'd m Y', | |
exclude_weekends : true, | |
exclude_dates : [], | |
local_months : [], | |
local_days : [], | |
initialize : function(options) { | |
this.element = options.element; | |
this.field = options.field || ''; | |
this.exclude_weekends = options.exclude_weekends == undefined ? this.exclude_weekends : options.exclude_weekends | |
this.fromDate = this._calDate(options.from , new Date()); | |
this.toDate = this._calDate(options.to); | |
this.setCurrent(this.fromDate); | |
if ( options.format ) { | |
this.format = options.format; | |
} | |
this.events = this._defaultEvents(options.events || {}); | |
this.exclude_dates = this._calExcludes(options.exclude_dates || []); | |
this.local_days = options.local_days || []; | |
this.local_months = options.local_months || []; | |
}, | |
_defaultEvents : function( event_options ) { | |
event_options.next_event = event_options.next_event || this.next_event; | |
event_options.back_event = event_options.back_event || this.back_event; | |
event_options.date_event = event_options.date_event || this.date_event; | |
event_options.self = this; | |
return event_options; | |
}, | |
draw : function( calanderObject ) { | |
var calander = -1; | |
if ( calanderObject == undefined ) { | |
calander = this.draw_helper(this.toDate, this.fromDate); | |
} | |
else { | |
calander = calanderObject | |
} | |
var element = $(this.element); | |
element.update(); | |
element.insert({top: calander.draw()}); | |
}, | |
draw_helper : function ( to , from , bind ) { | |
if ( bind == undefined ) { | |
bind = this; | |
} | |
return new bind.calander({'to':to , | |
'from':from , | |
events: bind.events , | |
exclude_weekends_flag: bind.exclude_weekends, | |
header_array : bind.local_days, | |
months_array : bind.local_months, | |
exclude_dates : bind.exclude_dates | |
}); | |
}, | |
_calExcludes : function( dates ) { | |
if ( dates.length < 1 ) return []; | |
var return_dates = []; | |
for ( var i =0; i < dates.length; i++) { | |
return_dates[i] = new Date(this._calDate(dates[i])); | |
} | |
return return_dates; | |
}, | |
_calDate : function(string , base) | |
{ | |
var date_array = -1; | |
if ( string.match(/^\d\D/) ) { | |
date_array = string.split(''); | |
var from = new Date(this.fromDate); | |
var additonal = parseInt(date_array[0]); | |
switch ( date_array[1]) { | |
// D is add additonal days | |
case 'd': | |
case 'D': | |
return new Date().setDate((base.getDate()|| from.getDate())+additonal) | |
break; | |
// W is add additional weeks | |
case 'w' : | |
case 'W' : | |
return new Date().setDate((base.getDate()|| from.getDate())+(additonal+7)) | |
break; | |
// M is add on additonal months | |
case 'm': | |
case 'M': | |
return new Date((base.getFullYear() || from.getFullYear()) , (base.getMonth() || from.getMonth())+additonal , 1); | |
break; | |
// Is the same as M but removes a specified number of months i.e 1f would go auguest to july; | |
case 'F': | |
case 'f': | |
return new Date((base.getFullYear() || from.getFullYear()) , (base.getMonth() || from.getMonth())-additonal , 1); | |
break; | |
} | |
} | |
else { | |
var date_sections = string.split(' '); | |
date_array = date_sections[0].split('-'); | |
if ( date_array.length > 3 ) throw ('A date string must be formated as YYYY-MM-DD not '+string); | |
// You have to correct for the offset months start at 0 not 1 so for example August is 7 not 8 | |
var date = new Date().setFullYear(date_array[0], (date_array[1]-1), date_array[2]) | |
return date | |
} | |
}, | |
setCurrent : function(date) { | |
this.current = new Date(date) | |
}, | |
getCurrent : function() { | |
return this.current | |
}, | |
next_event : function(e) { | |
var from = new Date(this.self._calDate('1m', this.self.getCurrent())); | |
if ( from.getMonth() > new Date(this.self.toDate).getMonth() ) { | |
alert('You can only select dates up to and including '+new Date(this.self.toDate).toLocaleDateString()); | |
return; | |
} | |
this.self.setCurrent(from); | |
this.self.draw(new this.self.draw_helper(this.self.toDate, from , this.self)); | |
}, | |
back_event : function(e) { | |
var from = new Date(this.self._calDate('1f', this.self.getCurrent())); | |
var base_from = new Date(this.self.fromDate) | |
if (from.getMonth() == base_from.getMonth() ) { | |
from.setDate(base_from.getDate()); | |
} | |
if ( from.getMonth() < new Date(this.self.fromDate).getMonth() ) { | |
alert('You can only select dates from and including '+new Date(this.self.fromDate).toLocaleDateString()); | |
return; | |
} | |
this.self.setCurrent(from); | |
this.self.draw(new this.self.draw_helper(this.self.toDate, from , this.self)); | |
}, | |
date_event : function(e) { | |
var element = $(e.element()); | |
var date_array = element.id.split('-'); | |
var parts = {d : date_array[0], | |
D : date_array[0].length < 2 ? '0'+date_array[0] : date_array[0], | |
m : date_array[1], | |
M : date_array[1].length < 2 ? '0'+date_array[1] : date_array[1], | |
Y : date_array[2], | |
y : date_array[2].slice(2)} | |
var date = this.self.format | |
$H(parts).each(function(format){ | |
date = date.replace(format.key , format.value); | |
}) | |
this.self.date = date | |
if ( this.self.field != '' && this.self.field != undefined ) { | |
$(this.self.field).value = this.self.date; | |
} | |
}, | |
calander : Class.create({ | |
to : -1, | |
from: -1, | |
calander : false, | |
current_day : 0, | |
row : false, | |
header_array : ['Su' , 'Mo' , 'Tu' , 'We' , 'Th' , 'Fr' , 'Sa'], | |
months_array : ['January' , 'February' , 'March' , 'April' , 'May' , 'June' , 'July' , 'August' , 'September' , 'October' , 'November' , 'December'], | |
exclude_dates : [], | |
exclude_weekends_flag : true, | |
started_flag : false, | |
events : -1, | |
initialize : function(options) { | |
this.to = options.to; | |
this.from = options.from; | |
this.calander = new Element('table').addClassName('calander'); | |
if ( options.exclude_weekends != undefined ) this.exclude_weekends_flag = options.exclude_weekends; | |
if ( options.header_array && options.header_array.length > 0 ) this.header_array = options.header_array; | |
if ( options.months_array && options.months_array.length > 0 ) this.months_array = options.months_array; | |
if ( options.events ) this.events = options.events; | |
if ( options.exclude_dates && options.exclude_dates.length > 0 ) this.exclude_dates = options.exclude_dates; | |
}, | |
header : function() { | |
var header = new Element('thead'); | |
var contents = new Element('tr'); | |
this.header_array.each(function(day){ | |
contents.insert({bottom: new Element('th').update(day)}); | |
}) | |
header.insert({bottom:contents}); | |
this.calander.insert({bottom: header}); | |
}, | |
footer : function() { | |
var self = this; | |
var footer = new Element('tfoot'); | |
var content = new Element('tr'); | |
var from = new Date(this.from); | |
if ( from.getMonth() > 0 ) { | |
content.insert({bottom : new Element('td').update('<').observe('click' , function(event) {self.trigger_event('back_event' , event)} )}) | |
} | |
else { | |
content.insert({bottom : new Element('td').update(' ')}) | |
} | |
content.insert({bottom : new Element('td' , {'colspan' : 5 , 'class' : 'month'}).update(this.months_array[from.getMonth()]+' '+from.getFullYear())}); | |
if ( from.getMonth() < (this.months_array.length -1) ) { | |
content.insert({bottom : new Element('td').update('>').observe('click' , function(event) {self.trigger_event('next_event' , event)})}) | |
} | |
else { | |
content.insert({bottom : new Element('td').update(' ')}) | |
} | |
footer.insert({bottom: content}); | |
this.calander.insert({bottom : footer}); | |
}, | |
trigger_event : function(event_name , event) { | |
if ( $(event.element()).hasClassName('inactive') ) { | |
event_name = 'inactive_'+event_name; | |
} | |
if ( this.events[event_name+'_before'] != undefined && this.events[event_name+'_before'] != -1) { | |
try { | |
this.events[event_name+'_before'](event) | |
} | |
catch (e) { | |
throw (e) | |
} | |
} | |
if ( this.events[event_name] != undefined && this.events[event_name] != -1) { | |
try { | |
this.events[event_name](event) | |
} | |
catch (e) { | |
throw (e); | |
} | |
} | |
else { | |
if ( console ) { | |
console.debug('No Event Called '+event_name) | |
} | |
} | |
if ( this.events[event_name+'_after'] != undefined && this.events[event_name+'_after'] != -1) { | |
try { | |
this.events[event_name+'_after'](event) | |
} | |
catch (e) { | |
throw (e) | |
} | |
} | |
}, | |
draw_range : function(from , to , cssClass) { | |
cssClass = cssClass || 'inactive'; | |
var start = new Date(from); | |
var self = this; | |
to = to == undefined ? 31 : new Date(to).getDate(); | |
for ( var i = 0; i <= to-2 ; i++) { | |
// Finish when we hit a new month and we are not on the first loop | |
// this is because a range may start on the 1st | |
if ( start.getDate() == 1 && i > 0) break; | |
// create new row after each week | |
if ( this.current_day == 0 ) { | |
if ( this.row ) { | |
this.calander.insert({bottom : this.row}); | |
} | |
this.row = new Element('tr'); | |
} | |
// This spaces the start date if its not a sunday | |
if (start.getDay() > 0 && this.started_flag == false) { | |
for ( var n = 0; n <= (start.getDay()-1) ; n++) { | |
this.row.insert({bottom : new Element('td').update(' ')}); | |
this.current_day = this.current_day == 6 ? 0 : this.current_day+1; | |
} | |
this.started_flag = true; | |
} | |
var timestamps = {from:new Date(this.from).getTime() , to:new Date(this.to).getTime()} | |
var active = ((start.getTime() >= timestamps.from && start.getTime() <= timestamps.to) && ((start.getDay() > 0 && start.getDay() < 6) || this.exclude_weekends_flag == false)) ? true : false; | |
if ( this.isExcluded(start) ) { | |
active = false; | |
} | |
var cellClass = active ? cssClass : 'inactive'; | |
this.row.insert({bottom : new Element('td' , {id:start.getDate()+'-'+(start.getMonth()+1)+'-'+start.getFullYear()}).addClassName(cellClass).update(start.getDate()).observe('click' , function(event){self.trigger_event('date_event' , event)})}); | |
start.setDate((start.getDate()+1)); | |
this.current_day = this.current_day == 6 ? 0 : this.current_day+1; | |
} | |
}, | |
isExcluded : function(date) { | |
if ( this.exclude_dates.length < 1 ) return false; | |
var excluded = false; | |
for ( var i=0; i <= this.exclude_dates.length-1; i++ ) { | |
if (date.getTime() == this.exclude_dates[i].getTime()) { | |
excluded = true; | |
} | |
} | |
return excluded; | |
}, | |
open : function() { | |
var from = new Date(this.from); | |
if (from.getDate() == 1) { | |
return false; | |
} | |
var prefix_date = new Date(this.from); | |
prefix_date.setDate(1); | |
this.draw_range(prefix_date , from , 'active') | |
return true; | |
}, | |
close : function() { | |
if ( this.current_day < 6 ) { | |
for (var d = this.current_day; d <= 6; d++){ | |
this.row.insert({bottom : new Element('td').update(' ')}); | |
} | |
} | |
}, | |
draw : function() { | |
this.header(); | |
this.footer(); | |
this.open(); | |
this.draw_range(this.from , undefined , 'active'); | |
this.close(); | |
this.calander.insert({bottom: this.row}); | |
return this.calander | |
} | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment