localization: start-of-week and right-to-left

This commit is contained in:
Adam Shaw 2009-05-08 19:34:50 +00:00
parent 7763b011f9
commit aea9e567ea
3 changed files with 172 additions and 100 deletions

View file

@ -35,7 +35,7 @@
{ {
id: 1, id: 1,
title: "Long Event", title: "Long Event",
start: new Date(y, m, 6), start: new Date(y, m, 6, 14, 0),
end: new Date(y, m, 11) end: new Date(y, m, 11)
}, },
{ {
@ -60,7 +60,14 @@
end: new Date(y, m, 29), end: new Date(y, m, 29),
url: "http://facebook.com/" url: "http://facebook.com/"
} }
] ],
weekStart: 1,
rightToLeft: true,
fixedWeeks: false,
title: true,
eventDrop: function(event, delta) {
//alert(delta);
}
}); });
}); });

View file

@ -2,22 +2,40 @@
/* top area w/ month title and buttons */ /* top area w/ month title and buttons */
.full-calendar-header { .full-calendar-header {
text-align: left; }
.full-calendar-title {
float: left;
margin-top: 0;
}
.r2l .full-calendar-title {
float: right;
} }
.full-calendar-buttons { .full-calendar-buttons {
float: right; float: right;
margin-bottom: 1em;
}
.r2l .full-calendar-buttons {
float: left;
} }
.full-calendar-buttons input { .full-calendar-buttons input {
vertical-align: middle; vertical-align: middle;
font-size: 1.0em; font-size: 1.0em;
margin-left: 5px;
}
.r2l .full-calendar-buttons input {
float: right;
margin: 0 5px 0 0;
} }
.full-calendar-prev, .full-calendar-prev,
.full-calendar-next { .full-calendar-next {
width: 40px; width: 40px;
margin-left: 5px;
} }
@ -31,6 +49,7 @@
reason for our long-winded border css. reason for our long-winded border css.
borders now look consistent across doctypes. */ borders now look consistent across doctypes. */
border: 1px solid #ccc; /* border color & style */ border: 1px solid #ccc; /* border color & style */
text-align: left;
} }
.full-calendar-month table { .full-calendar-month table {
@ -38,7 +57,7 @@
border-spacing: 0; border-spacing: 0;
} }
.full-calendar-month th.day-heading, .full-calendar-month th,
.full-calendar-month td.day { .full-calendar-month td.day {
padding: 0; padding: 0;
vertical-align: top; vertical-align: top;
@ -47,12 +66,13 @@
border-width: 1px 0 0 1px; border-width: 1px 0 0 1px;
} }
.full-calendar-month tr.day-headings th { .full-calendar-month th {
border-top: 0; border-top: 0;
text-align: center;
} }
.full-calendar-month th.sun, .full-calendar-month th.first, /* left edge? */
.full-calendar-month td.sun { .full-calendar-month td.first {
border-left: 0; border-left: 0;
} }
@ -66,7 +86,11 @@
.full-calendar-month .day-number { .full-calendar-month .day-number {
text-align: right; text-align: right;
padding-right: 2px; padding: 0 2px;
}
.r2l .full-calendar-month .day-number {
text-align: left;
} }
.full-calendar-month .other-month .day-number { .full-calendar-month .other-month .day-number {
@ -93,6 +117,10 @@
text-align: left; text-align: left;
} }
.r2l .full-calendar-month .event {
text-align: right;
}
.full-calendar-month .ui-draggable-dragging td { .full-calendar-month .ui-draggable-dragging td {
cursor: move; cursor: move;
} }

View file

@ -29,7 +29,19 @@
options = options || {}; options = options || {};
var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime;
var bo = options.buttons; var bo = typeof options.buttons == 'undefined' ? true : options.buttons;
var weekStart = (options.weekStart || 0) % 7;
var r2l = options.rightToLeft;
var dis, dit; // day index sign / translate
if (r2l) {
dis = -1;
dit = 6;
this.addClass('r2l');
}else{
dis = 1;
dit = 0;
}
this.each(function() { this.each(function() {
@ -80,40 +92,31 @@
var titleElement, todayButton, monthElement; var titleElement, todayButton, monthElement;
var header = $("<div class='full-calendar-header'/>").appendTo(this); var header = $("<div class='full-calendar-header'/>").appendTo(this);
if (bo != false) {
var buttons = $("<div class='full-calendar-buttons'/>").appendTo(header);
todayButton =
$("<input type='button' class='full-calendar-today' value='today'/>")
.click(today);
var prevButton =
$("<input type='button' class='full-calendar-prev' value='&lt;'/>")
.click(prevMonth);
var nextButton =
$("<input type='button' class='full-calendar-next' value='&gt;'/>")
.click(nextMonth);
if (typeof bo == 'object') {
if (bo.today != false) {
if (typeof bo.today == 'string') todayButton.val(bo.today);
buttons.append(todayButton);
}
if (bo.prev != false) {
if (typeof bo.prev == 'string') prevButton.val(bo.prev);
buttons.append(prevButton);
}
if (bo.next != false) {
if (typeof bo.next == 'string') nextButton.val(bo.next);
buttons.append(nextButton);
}
}else{
buttons
.append(todayButton)
.append(prevButton)
.append(nextButton);
}
}
if (options.title !== false) if (options.title !== false)
titleElement = $("<h2 class='full-calendar-title'/>").appendTo(header); titleElement = $("<h2 class='full-calendar-title'/>").appendTo(header);
if (bo) {
var buttons = $("<div class='full-calendar-buttons'/>").appendTo(header);
var prevButton, nextButton;
if (bo == true || bo.today != false) {
todayButton = $("<input type='button' class='full-calendar-today' value='today'/>")
.appendTo(buttons)
.click(today);
if (typeof bo.today == 'string') todayButton.val(bo.today);
}
if (bo == true || bo.prev != false) {
prevButton = $("<input type='button' class='full-calendar-prev' value='" + (r2l ? "&gt;" : "&lt;") + "'/>")
.appendTo(buttons)
.click(prevMonth);
if (typeof bo.prev == 'string') prevButton.val(bo.prev);
}
if (bo == true || bo.next != false) {
nextButton = $("<input type='button' class='full-calendar-next' value='" + (r2l ? "&lt;" : "&gt;") + "'/>")
.appendTo(buttons)
.click(nextMonth);
if (typeof bo.next == 'string') nextButton.val(bo.next);
}
}
monthElement = $("<div class='full-calendar-month' style='position:relative'/>").appendTo(this); monthElement = $("<div class='full-calendar-month' style='position:relative'/>").appendTo(this);
@ -122,7 +125,7 @@
var tbody, glass, monthTitle; var thead, tbody, glass, monthTitle;
function render() { function render() {
@ -136,10 +139,10 @@
clearTime(date); clearTime(date);
start = cloneDate(date); start = cloneDate(date);
addDays(start, -start.getDay()); addDays(start, -start.getDay() + weekStart);
end = cloneDate(date); end = cloneDate(date);
addMonths(end, 1); addMonths(end, 1);
addDays(end, (7 - end.getDay()) % 7); addDays(end, (7 - end.getDay() + weekStart) % 7);
numWeeks = Math.round((end.getTime() - start.getTime()) / 604800000); numWeeks = Math.round((end.getTime() - start.getTime()) / 604800000);
if (options.fixedWeeks != false) { if (options.fixedWeeks != false) {
addDays(end, (6 - numWeeks) * 7); addDays(end, (6 - numWeeks) * 7);
@ -156,32 +159,40 @@
} }
if (!tbody) { if (!tbody) {
tbody = "<tbody><tr class='day-headings'>"; var table = $("<table style='width:100%'/>").appendTo(monthElement);
for (var i=0; i<7; i++) {
tbody += thead = "<thead><tr>";
"<th class='day-heading " + dayAbbrevs[i].toLowerCase() + "'>" + for (var i=0; i<7; i++) {
(options.abbrevDayHeadings!=false ? dayAbbrevs[i] : dayNames[i]) + var j = (i * dis + dit + weekStart) % 7;
thead +=
"<th class='" + dayAbbrevs[j].toLowerCase() +
(i==0 ? ' first' : '') + "'>" +
(options.abbrevDayHeadings!=false ? dayAbbrevs[j] : dayNames[j]) +
"</th>"; "</th>";
} }
thead = $(thead + "</tr></thead>").appendTo(table);
tbody = "<tbody>";
var d = cloneDate(start); var d = cloneDate(start);
for (var i=0; i<numWeeks; i++) { for (var i=0; i<numWeeks; i++) {
tbody += "<tr class='week"+(i+1)+"'>"; tbody += "<tr class='week"+(i+1)+"'>";
var tds = "";
for (var j=0; j<7; j++) { for (var j=0; j<7; j++) {
tbody += var s =
"<td class='day " + dayAbbrevs[j].toLowerCase() + "<td class='day " + dayAbbrevs[(j + weekStart) % 7].toLowerCase() +
(j==dit ? ' first' : '') +
(d.getMonth() == month ? '' : ' other-month') + (d.getMonth() == month ? '' : ' other-month') +
(d.getTime() == today.getTime() ? ' today' : '') + (d.getTime() == today.getTime() ? ' today' : '') +
"'><div class='day-number'>" + d.getDate() + "</div>" + "'><div class='day-number'>" + d.getDate() + "</div>" +
"<div class='day-content'><div/></div></td>"; "<div class='day-content'><div/></div></td>";
if (r2l) tds = s + tds;
else tds += s;
addDays(d, 1); addDays(d, 1);
} }
tbody += "</tr>"; tbody += tds + "</tr>";
} }
tbody += "</tr></tbody>"; tbody = $(tbody + "</tbody>").appendTo(table);
tbody = $(tbody)
.appendTo($("<table style='width:100%'/>")
.appendTo(monthElement));
glass = $("<div style='position:absolute;top:0;left:0;z-index:1;width:100%' />") glass = $("<div style='position:absolute;top:0;left:0;z-index:1;width:100%' />")
.appendTo(monthElement) .appendTo(monthElement)
@ -195,9 +206,9 @@
}else{ }else{
var diff = numWeeks - (tbody.find('tr').length - 1); var diff = numWeeks - tbody.find('tr').length;
if (diff < 0) { if (diff < 0) {
tbody.find('tr:gt(' + numWeeks + ')').remove(); tbody.find('tr:gt(' + (numWeeks-1) + ')').remove();
} }
else if (diff > 0) { else if (diff > 0) {
var trs = ""; var trs = "";
@ -205,7 +216,9 @@
trs += "<tr class='week"+(numWeeks+i)+"'>"; trs += "<tr class='week"+(numWeeks+i)+"'>";
for (var j=0; j<7; j++) { for (var j=0; j<7; j++) {
trs += trs +=
"<td class='day " + dayAbbrevs[j].toLowerCase() + "'>" + "<td class='day " +
dayAbbrevs[(j * dis + dit + weekStart) % 7].toLowerCase() +
(j==0 ? ' first' : '') + "'>" +
"<div class='day-number'></div>" + "<div class='day-number'></div>" +
"<div class='day-content'><div/></div>" + "<div class='day-content'><div/></div>" +
"</td>"; "</td>";
@ -216,19 +229,22 @@
} }
var d = cloneDate(start); var d = cloneDate(start);
tbody.find('td').each(function() { tbody.find('tr').each(function() {
if (d.getMonth() == month) { for (var i=0; i<7; i++) {
$(this).removeClass('other-month'); var td = this.childNodes[i * dis + dit];
}else{ if (d.getMonth() == month) {
$(this).addClass('other-month'); $(td).removeClass('other-month');
}else{
$(td).addClass('other-month');
}
if (d.getTime() == today.getTime()) {
$(td).addClass('today');
}else{
$(td).removeClass('today');
}
$(td.childNodes[0]).text(d.getDate());
addDays(d, 1);
} }
if (d.getTime() == today.getTime()) {
$(this).addClass('today');
}else{
$(this).removeClass('today');
}
$(this.childNodes[0]).text(d.getDate());
addDays(d, 1);
}); });
} }
@ -240,7 +256,7 @@
var jsonOptions = {}; var jsonOptions = {};
jsonOptions[options.startParam || 'start'] = Math.round(start.getTime() / 1000); jsonOptions[options.startParam || 'start'] = Math.round(start.getTime() / 1000);
jsonOptions[options.endParam || 'end'] = Math.round(end.getTime() / 1000); jsonOptions[options.endParam || 'end'] = Math.round(end.getTime() / 1000);
jsonOptions['_t'] = (new Date()).getTime(); jsonOptions[options.cacheParam || '_t'] = (new Date()).getTime();
$.getJSON(options.events, jsonOptions, function(data) { $.getJSON(options.events, jsonOptions, function(data) {
events = cleanEvents(data); events = cleanEvents(data);
renderEvents(events); renderEvents(events);
@ -345,7 +361,7 @@
function _renderEvents() { function _renderEvents() {
for (var i=0; i<eventMatrix.length; i++) { for (var i=0; i<eventMatrix.length; i++) {
var levels = eventMatrix[i]; var levels = eventMatrix[i];
var tr = tbody.find('tr:eq('+(i+1)+')'); var tr = tbody.find('tr:eq('+i+')');
var innerDiv = tr.find('td:first div.day-content div'); var innerDiv = tr.find('td:first div.day-content div');
var top = innerDiv.position().top; var top = innerDiv.position().top;
var height = 0; var height = 0;
@ -355,28 +371,42 @@
for (var k=0; k<segs.length; k++) { for (var k=0; k<segs.length; k++) {
var seg = segs[k]; var seg = segs[k];
var event = seg.event; var event = seg.event;
var left1 = seg.isStart ? var left1, left2, roundW, roundE;
tr.find('td:eq('+seg.start.getDay()+') div.day-content div').position().left : if (r2l) {
tbody.position().left; left2 = seg.isStart ?
var left2 = seg.isEnd ? tr.find('td:eq('+((seg.start.getDay()-weekStart+7)%7*dis+dit)+') div.day-content div') :
tr.find('td:eq('+((seg.end.getDay()+6)%7)+') div.day-content div') : tbody;
tbody; left1 = seg.isEnd ?
tr.find('td:eq('+((seg.end.getDay()+6-weekStart)%7*dis+dit)+') div.day-content div').position().left :
tbody.position().left;
roundW = seg.isEnd;
roundE = seg.isStart;
}else{
left1 = seg.isStart ?
tr.find('td:eq('+((seg.start.getDay()-weekStart+7)%7)+') div.day-content div').position().left :
tbody.position().left;
left2 = seg.isEnd ?
tr.find('td:eq('+((seg.end.getDay()+6-weekStart)%7)+') div.day-content div') :
tbody;
roundW = seg.isStart;
roundE = seg.isEnd;
}
left2 = left2.position().left + left2.width(); left2 = left2.position().left + left2.width();
var element = $("<table class='event' />") var element = $("<table class='event' />")
.append("<tr>" + .append("<tr>" +
(seg.isStart ? "<td class='nw'/>" : '') + (roundW ? "<td class='nw'/>" : '') +
"<td class='n'/>" + "<td class='n'/>" +
(seg.isEnd ? "<td class='ne'/>" : '') + "</tr>") (roundE ? "<td class='ne'/>" : '') + "</tr>")
.append("<tr>" + .append("<tr>" +
(seg.isStart ? "<td class='w'/>" : '') + (roundW ? "<td class='w'/>" : '') +
"<td class='c'/>" + "<td class='c'/>" +
(seg.isEnd ? "<td class='e'/>" : '') + "</tr>") (roundE ? "<td class='e'/>" : '') + "</tr>")
.append("<tr>" + .append("<tr>" +
(seg.isStart ? "<td class='sw'/>" : '') + (roundW ? "<td class='sw'/>" : '') +
"<td class='s'/>" + "<td class='s'/>" +
(seg.isEnd ? "<td class='se'/>" : '') + "</tr>"); (roundE ? "<td class='se'/>" : '') + "</tr>");
buildEventText(element.find('td.c'), event, buildEventText(element.find('td.c'), event,
typeof event.showTime == 'undefined' ? showTime : event.showTime); typeof event.showTime == 'undefined' ? showTime : event.showTime, r2l);
if (options.eventRender) { if (options.eventRender) {
var res = options.eventRender(event, element); var res = options.eventRender(event, element);
if (typeof res != 'undefined') { if (typeof res != 'undefined') {
@ -529,7 +559,7 @@
dayX0 = o.left; dayX0 = o.left;
dayY0 = o.top; dayY0 = o.top;
dayY = []; dayY = [];
tbody.find('tr:gt(0)').each(function() { tbody.find('tr').each(function() {
tr = $(this); tr = $(this);
dayY.push(tr.position().top); dayY.push(tr.position().top);
}); });
@ -553,7 +583,7 @@
else if (!currTD || r != currR || c != currC) { else if (!currTD || r != currR || c != currC) {
currR = r; currR = r;
currC = c; currC = c;
currTD = tbody.find('tr:eq('+(r+1)+') td:eq('+c+')').get(0); currTD = tbody.find('tr:eq('+r+') td:eq('+c+')').get(0);
currTDX = dayX[c]; currTDX = dayX[c];
currTDY = dayY[r]; currTDY = dayY[r];
currTDW = dayX[c+1] - currTDX; currTDW = dayX[c+1] - currTDX;
@ -573,10 +603,14 @@
} }
function dayDelta(node1, node2) { function dayDelta(node1, node2) {
var i1, i2, tds = tbody.get(0).getElementsByTagName('td'); var i1, i2, trs = tbody.get(0).getElementsByTagName('tr');
for (var i=0; i<tds.length; i++) { for (var i=0; i<trs.length; i++) {
if (tds[i] == node1) i1 = i; var tr = trs[i];
if (tds[i] == node2) i2 = i; for (var j=0; j<7; j++) {
var td = tr.childNodes[j];
if (td == node1) i1 = i*7 + j*dis + dit;
if (td == node2) i2 = i*7 + j*dis + dit;
}
} }
return i2 - i1; return i2 - i1;
} }
@ -589,7 +623,7 @@
function resizeTable() { function resizeTable() {
var cellw = Math.floor(tbody.width() / 7); var cellw = Math.floor(tbody.width() / 7);
var cellh = Math.round(cellw * .85); var cellh = Math.round(cellw * .85);
tbody.find('th:lt(6)').width(cellw); thead.find('th:lt(6)').width(cellw);
tbody.find('td').height(cellh); tbody.find('td').height(cellh);
glass.height(monthElement.height()); glass.height(monthElement.height());
} }
@ -624,18 +658,21 @@
// event utils // event utils
function buildEventText(element, event, showTime) { function buildEventText(element, event, showTime, r2l) {
if (showTime != false) { if (showTime != false) {
var h = event.start.getHours(); var h = event.start.getHours();
var m = event.start.getMinutes(); var m = event.start.getMinutes();
if (showTime == true || showTime == 'guess' && if (showTime == true || showTime == 'guess' &&
(h || m || event.end.getHours() || event.end.getMinutes())) { (h || m || event.end.getHours() || event.end.getMinutes())) {
element.append($("<span class='event-time' />") var timeText = (h%12 || 12) + (h<12 ? 'a' : 'p');
.text((h%12 || 12) + (h<12 ? 'a' : 'p') + ' ')); if (r2l) timeText = ' ' + timeText;
else timeText += ' ';
element.append($("<span class='event-time' />").text(timeText));
} }
} }
element.append($("<span class='event-title' />") var et = $("<span class='event-title' />").text(event.title)
.text(event.title)); if (r2l) element.prepend(et);
else element.append(et);
} }