Skip to content

Instantly share code, notes, and snippets.

@jimjeffers
Created March 29, 2010 06:13
Show Gist options
  • Select an option

  • Save jimjeffers/347496 to your computer and use it in GitHub Desktop.

Select an option

Save jimjeffers/347496 to your computer and use it in GitHub Desktop.
A simple calendar input that only has essential functionality.
jQuery.fn.calendarInput: (options) ->
defaults: {
active_class: "active"
calendar_id: "calendar_table"
calendar_class: "calendar_object"
calendar_container_id: "calendar_container"
controls_class: "calendar_controls"
custom_class: false
default_class: "calendar_input"
dont_allow_dates_from_the_past: true
inactive_class: "inactive"
indicator_class: "calendar_indicator"
prior_class: "prior_month"
prior_button_class: "prior_button"
month_name_class: "month_name"
next_class: "next_month"
next_button_class: "next_button"
reset_class: "calendar_reset"
wrapper: "<span>"
}
settings: jQuery.extend(defaults,options)
class CalendarController
constructor: ->
@date_pattern = /(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/
@current_date = new Date()
@today = new Date()
@present_day: @today.getDate()
@present_month: @today.getMonth()
@weekdays = [
"sun"
"mon"
"tue"
"wed"
"thr"
"fri"
"sat"
]
@months = [
"January"
"February"
"March"
"April"
"May"
"June"
"July"
"August"
"September"
"October"
"Novemeber"
"December"
]
# Generate the table if it doesn't exist yet. Since we rely on an ID this is
# technically a singleton pattern imposed via the DOM.
@calendar_table = $("#"+settings.calendar_id)
if @calendar_table.length < 1
$("body").append("<div id=\""+settings.calendar_container_id+"\"><table id=\""+settings.calendar_id+"\" class=\""+settings.calendar_class+"\"></table></div>")
$("#"+settings.calendar_container_id).prepend("<ul class=\""+settings.controls_class+"\"><li class=\""+settings.next_button_class+"\"><a href=\"#\">&gt;</a></li><li class=\""+settings.prior_button_class+"\"><a href=\"#\">&lt;</a></li><li class=\""+settings.month_name_class+"\"></li></ul>")
@container = $("#"+settings.calendar_container_id).addClass("inactive")
@calendar_table = $("#"+settings.calendar_id)
@calendar_controls = $("."+settings.controls_class)
@calendar_controls.delegate("a", "click", (e) =>
link: $(e.target)
if link.parent().hasClass(settings.prior_button_class)
this.backward()
else if link.parent().hasClass(settings.next_button_class)
this.forward()
false
)
@month_name = @calendar_controls.find("."+settings.month_name_class)
@next_button = @calendar_controls.find("."+settings.next_button_class)
@prior_button = @calendar_controls.find("."+settings.prior_button_class)
# Add event delegation to listen for clicks.
@calendar_table.delegate("a", "click", (e) =>
@current_date = new Date(@current_date.getFullYear(), @current_date.getMonth(),$(e.target).html())
this.update_input()
this.deactivate()
false
)
# Build calendar object html.
for week in [0..5]
if week > 0
@calendar_table.append("<tr class=\"week\"></tr>")
row: @calendar_table.find("tr:last-child")
else
@calendar_table.append("<th class=\"week\"></th>")
row: @calendar_table.find("th:first-child")
for day in [0..6]
if week > 0
row.append("<td class=\"day "+@weekdays[day]+"\"></td>")
else
row.append("<td class=\"day "+@weekdays[day]+"\">"+@weekdays[day]+"</td>")
# Set the initial date to the current day.
this.set(@current_date.getFullYear(), @current_date.getMonth(), @current_date.getDate())
# Bind event listener to close the calendar input if the user clicks off the element.
$("body").click( (e) =>
this.deactivate() if @container.hasClass(settings.active_class)
)
set: (year,month,day) ->
@current_date = new Date(year,month-1,day)
@month_name.html(@months[@current_date.getMonth()]+" "+@current_date.getFullYear())
month_start: new Date(@current_date.getFullYear(), @current_date.getMonth(),1)
month_end: new Date(@current_date.getFullYear(), @current_date.getMonth()+1,0)
month_days: [1..month_end.getDate()]
if month_start.getDay() > 0
prior_month_end: new Date(@current_date.getFullYear(), @current_date.getMonth(),0)
prior_month_start: new Date(@current_date.getFullYear(), @current_date.getMonth(),(-month_start.getDay()+1))
prior_month_days: [prior_month_start.getDate()..prior_month_end.getDate()]
else
prior_month_days: []
next_month_end: new Date(@current_date.getFullYear(), @current_date.getMonth()+3,0)
next_month_start: new Date(@current_date.getFullYear(), @current_date.getMonth()+2,1)
next_month_days: [next_month_start.getDate()..next_month_end.getDate()]
@current_month: @current_date.getMonth()
cells: $("tr td.day")
cells.removeClass(settings.next_class).removeClass(settings.prior_class).each( (index) =>
cell: $(cells.get(index))
if index < prior_month_days.length
cell.html(prior_month_days[index]).addClass(settings.prior_class)
else if index < month_days.length + prior_month_days.length
date_active: true
day_number: month_days[index-prior_month_days.length]
if settings.dont_allow_dates_from_the_past and day_number < @present_day and @current_month == @present_month
date_active = false
else if settings.dont_allow_dates_from_the_past and @current_month < @present_month
date_active = false
if date_active
cell.html("<a href=\"#\">"+month_days[index-prior_month_days.length]+"</a>")
else
cell.html(month_days[index-prior_month_days.length]).addClass(settings.inactive_class)
else
cell.html(next_month_days[index-prior_month_days.length-month_days.length]).addClass(settings.next_class)
)
set_from_input: (value) ->
if @date_pattern.test(value)
values: value.split("/")
this.set(parseInt(values[2]), parseInt(values[0]), parseInt(values[1]))
forward: ->
this.set(@current_date.getFullYear(), @current_date.getMonth()+2, 1)
backward: ->
prior_month: @current_date.getMonth()-1
if settings.dont_allow_dates_from_the_past
if current_month >= @present_month
this.set(@current_date.getFullYear(), @current_date.getMonth(), 1)
if current_month == @present_month
@prior_button.addClass(settings.inactive_class)
else
this.set(@current_date.getFullYear(), @current_date.getMonth(), 1)
activate: (input) ->
@input = input
this.snap_to_input()
this.set_from_input(input.val()) if input.val()
@container.addClass(settings.active_class).removeClass(settings.inactive_class)
deactivate: () ->
@container.removeClass(settings.active_class).addClass(settings.inactive_class)
snap_to_input: () ->
coordinates: @input.offset()
display_height: parseInt(@input.css("padding-bottom")) +
parseInt(@input.css("padding-top")) +
@input.height()
@container.css("top",coordinates.top+display_height+10+"px")
@container.css("left",coordinates.left+"px")
update_input: () ->
day: @current_date.getDate()
month: @current_date.getMonth()+1
year: @current_date.getFullYear()
if day < 10
day = "0"+day
if month < 10
month = "0"+month
@input.val(month+"/"+day+"/"+year)
calendar_controller: new CalendarController()
this.each( ->
input: $(this)
input.wrap(settings.wrapper)
input.click( (e) ->
input: $(e.target)
calendar_controller.activate(input)
false
)
container: input.parent().addClass(settings.default_class)
container.addClass(settings.custom_class) if settings.custom_class
container.append("<a href=\"#\"></a>")
input.next().addClass(settings.indicator_class).click( (e) ->
input: $(e.target).parent().find("input")
calendar_controller.activate(input)
false
)
)
(function(){
jQuery.fn.calendarInput = function calendarInput(options) {
var CalendarController, calendar_controller, defaults, settings;
defaults = {
active_class: "active",
calendar_id: "calendar_table",
calendar_class: "calendar_object",
calendar_container_id: "calendar_container",
controls_class: "calendar_controls",
custom_class: false,
default_class: "calendar_input",
dont_allow_dates_from_the_past: true,
inactive_class: "inactive",
indicator_class: "calendar_indicator",
prior_class: "prior_month",
prior_button_class: "prior_button",
month_name_class: "month_name",
next_class: "next_month",
next_button_class: "next_button",
reset_class: "calendar_reset",
wrapper: "<span>"
};
settings = jQuery.extend(defaults, options);
CalendarController = function CalendarController() {
var _a, _b, _c, _d, _e, _f, _g, _h, day, row, week;
this.current_date = new Date();
this.today = new Date();
this.present_day = this.today.getDate();
this.present_month = this.today.getMonth();
this.weekdays = ["sun", "mon", "tue", "wed", "thr", "fri", "sat"];
this.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "Novemeber", "December"];
this.date_pattern = /(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/;
// Generate the table if it doesn't exist yet. Since we rely on an ID this is
// technically a singleton pattern imposed via the DOM.
this.calendar_table = $("#" + settings.calendar_id);
if (this.calendar_table.length < 1) {
$("body").append("<div id=\"" + settings.calendar_container_id + "\"><table id=\"" + settings.calendar_id + "\" class=\"" + settings.calendar_class + "\"></table></div>");
$("#" + settings.calendar_container_id).prepend("<ul class=\"" + settings.controls_class + "\"><li class=\"" + settings.next_button_class + "\"><a href=\"#\">&gt;</a></li><li class=\"" + settings.prior_button_class + "\"><a href=\"#\">&lt;</a></li><li class=\"" + settings.month_name_class + "\"></li></ul>");
this.container = $("#" + settings.calendar_container_id).addClass("inactive");
this.calendar_table = $("#" + settings.calendar_id);
this.calendar_controls = $("." + settings.controls_class);
this.calendar_controls.delegate("a", "click", (function(__this) {
var __func = function(e) {
var link;
link = $(e.target);
if (link.parent().hasClass(settings.prior_button_class)) {
this.backward();
} else if (link.parent().hasClass(settings.next_button_class)) {
this.forward();
}
return false;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
this.month_name = this.calendar_controls.find("." + settings.month_name_class);
this.next_button = this.calendar_controls.find("." + settings.next_button_class);
this.prior_button = this.calendar_controls.find("." + settings.prior_button_class);
// Add event delegation to listen for clicks.
this.calendar_table.delegate("a", "click", (function(__this) {
var __func = function(e) {
this.current_date = new Date(this.current_date.getFullYear(), this.current_date.getMonth(), $(e.target).html());
this.update_input();
this.deactivate();
return false;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
// Build calendar object html.
_c = 0; _d = 5;
for (_b = 0, week = _c; (_c <= _d ? week <= _d : week >= _d); (_c <= _d ? week += 1 : week -= 1), _b++) {
if (week > 0) {
this.calendar_table.append("<tr class=\"week\"></tr>");
row = this.calendar_table.find("tr:last-child");
} else {
this.calendar_table.append("<th class=\"week\"></th>");
row = this.calendar_table.find("th:first-child");
}
_g = 0; _h = 6;
for (_f = 0, day = _g; (_g <= _h ? day <= _h : day >= _h); (_g <= _h ? day += 1 : day -= 1), _f++) {
week > 0 ? row.append("<td class=\"day " + this.weekdays[day] + "\"></td>") : row.append("<td class=\"day " + this.weekdays[day] + "\">" + this.weekdays[day] + "</td>");
}
}
}
// Set the initial date to the current day.
this.set(this.current_date.getFullYear(), this.current_date.getMonth(), this.current_date.getDate());
// Bind event listener to close the calendar input if the user clicks off the element.
$("body").click((function(__this) {
var __func = function(e) {
if (this.container.hasClass(settings.active_class)) {
return this.deactivate();
}
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
return this;
};
CalendarController.prototype.set = function set(year, month, day) {
var _a, _b, _c, cells, month_days, month_end, month_start, next_month_days, next_month_end, next_month_start, prior_month_days, prior_month_end, prior_month_start;
this.current_date = new Date(year, month - 1, day);
this.month_name.html(this.months[this.current_date.getMonth()] + " " + this.current_date.getFullYear());
month_start = new Date(this.current_date.getFullYear(), this.current_date.getMonth(), 1);
month_end = new Date(this.current_date.getFullYear(), this.current_date.getMonth() + 1, 0);
month_days = (function() {
var _b, _c, _d, _e, _f;
_b = []; _e = 1; _f = month_end.getDate();
for (_d = 0, _a = _e; (_e <= _f ? _a <= _f : _a >= _f); (_e <= _f ? _a += 1 : _a -= 1), _d++) {
_b.push(_a);
}
return _b;
}());
if (month_start.getDay() > 0) {
prior_month_end = new Date(this.current_date.getFullYear(), this.current_date.getMonth(), 0);
prior_month_start = new Date(this.current_date.getFullYear(), this.current_date.getMonth(), (-month_start.getDay() + 1));
prior_month_days = (function() {
var _c, _d, _e, _f, _g;
_c = []; _f = prior_month_start.getDate(); _g = prior_month_end.getDate();
for (_e = 0, _b = _f; (_f <= _g ? _b <= _g : _b >= _g); (_f <= _g ? _b += 1 : _b -= 1), _e++) {
_c.push(_b);
}
return _c;
}());
} else {
prior_month_days = [];
}
next_month_end = new Date(this.current_date.getFullYear(), this.current_date.getMonth() + 3, 0);
next_month_start = new Date(this.current_date.getFullYear(), this.current_date.getMonth() + 2, 1);
next_month_days = (function() {
var _d, _e, _f, _g, _h;
_d = []; _g = next_month_start.getDate(); _h = next_month_end.getDate();
for (_f = 0, _c = _g; (_g <= _h ? _c <= _h : _c >= _h); (_g <= _h ? _c += 1 : _c -= 1), _f++) {
_d.push(_c);
}
return _d;
}());
this.current_month = this.current_date.getMonth();
cells = $("tr td.day");
return cells.removeClass(settings.next_class).removeClass(settings.prior_class).each((function(__this) {
var __func = function(index) {
var cell, date_active, day_number;
cell = $(cells.get(index));
if (index < prior_month_days.length) {
return cell.html(prior_month_days[index]).addClass(settings.prior_class);
} else if (index < month_days.length + prior_month_days.length) {
date_active = true;
day_number = month_days[index - prior_month_days.length];
if (settings.dont_allow_dates_from_the_past && day_number < this.present_day && this.current_month === this.present_month) {
date_active = false;
} else if (settings.dont_allow_dates_from_the_past && this.current_month < this.present_month) {
date_active = false;
}
return date_active ? cell.html("<a href=\"#\">" + month_days[index - prior_month_days.length] + "</a>") : cell.html(month_days[index - prior_month_days.length]).addClass(settings.inactive_class);
} else {
return cell.html(next_month_days[index - prior_month_days.length - month_days.length]).addClass(settings.next_class);
}
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
};
CalendarController.prototype.set_from_input = function set_from_input(value) {
var values;
if (this.date_pattern.test(value)) {
values = value.split("/");
return this.set(parseInt(values[2]), parseInt(values[0]), parseInt(values[1]));
}
};
CalendarController.prototype.forward = function forward() {
return this.set(this.current_date.getFullYear(), this.current_date.getMonth() + 2, 1);
};
CalendarController.prototype.backward = function backward() {
var prior_month;
prior_month = this.current_date.getMonth() - 1;
if (settings.dont_allow_dates_from_the_past) {
current_month >= this.present_month ? this.set(this.current_date.getFullYear(), this.current_date.getMonth(), 1) : null;
return current_month === this.present_month ? this.prior_button.addClass(settings.inactive_class) : null;
} else {
return this.set(this.current_date.getFullYear(), this.current_date.getMonth(), 1);
}
};
CalendarController.prototype.activate = function activate(input) {
this.input = input;
this.snap_to_input();
if (input.val()) {
this.set_from_input(input.val());
}
return this.container.addClass(settings.active_class).removeClass(settings.inactive_class);
};
CalendarController.prototype.deactivate = function deactivate() {
return this.container.removeClass(settings.active_class).addClass(settings.inactive_class);
};
CalendarController.prototype.snap_to_input = function snap_to_input() {
var coordinates, display_height;
coordinates = this.input.offset();
display_height = parseInt(this.input.css("padding-bottom")) + parseInt(this.input.css("padding-top")) + this.input.height();
this.container.css("top", coordinates.top + display_height + 10 + "px");
return this.container.css("left", coordinates.left + "px");
};
CalendarController.prototype.update_input = function update_input() {
var day, month, year;
day = this.current_date.getDate();
month = this.current_date.getMonth() + 1;
year = this.current_date.getFullYear();
day < 10 ? (day = "0" + day) : null;
month < 10 ? (month = "0" + month) : null;
return this.input.val(month + "/" + day + "/" + year);
};
calendar_controller = new CalendarController();
return this.each(function() {
var container, input;
input = $(this);
input.wrap(settings.wrapper);
input.click(function(e) {
input = $(e.target);
calendar_controller.activate(input);
return false;
});
container = input.parent().addClass(settings.default_class);
if (settings.custom_class) {
container.addClass(settings.custom_class);
}
container.append("<a href=\"#\"></a>");
return input.next().addClass(settings.indicator_class).click(function(e) {
input = $(e.target).parent().find("input");
calendar_controller.activate(input);
return false;
});
});
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment