Skip to content

Instantly share code, notes, and snippets.

@myaumyau
Created February 18, 2013 04:02
Show Gist options
  • Save myaumyau/4975049 to your computer and use it in GitHub Desktop.
Save myaumyau/4975049 to your computer and use it in GitHub Desktop.
[js]自前カレンダー
Calendar = function() {
this.initialize.apply(this, arguments);
}
Calendar.config = {
tags: {
header: "table",
headerChild: "tr",
thisMonth: "td",
prev: "td",
next: "td",
month: "table",
dayOfWeek: "th",
week: "tr",
day: "td"
},
thisMonthFormat: "yyyy年MM月",
selectedFormat: "yyyy/MM/dd",
dayOfWeekNames: [ "日", "月", "火", "水", "木", "金", "土" ],
noClickDate: null
}
Calendar.prototype = {
/* Methods */
initialize: function(config) {
if (!config["textboxId"]) throw("not setting textboxId.");
if (!config["triggerId"]) throw("not setting triggerId.");
if (!config["areaId"]) throw("not setting areaId.");
this._textNode = this.getNode(config["textboxId"]);
if (!this._textNode) throw("not found textboxId node.");
this._triggerNode = this.getNode(config["triggerId"]);
if (!this._triggerNode) throw("not found triggerId node.");
this._areaId = config["areaId"];
this._config = {};
for( var key in Calendar.config ) {
if (!config[key]) {
this._config[key] = Calendar.config[key];
} else {
this._config[key] = config[key];
}
}
this._uniqueId = "Calendar_" + DateTime.now().totalMilliseconds();
this._today = DateTime.today();
this._selectedDate = this._today;
this._currentMonth = new DateTime(this._today.year(), this._today.month(), 1);
this._visible = false;
this._iframe = null;
this._div = null;
this.hide();
this.update();
var __this = this;
// regster event
addEvent(this._triggerNode, "click", function() {
__this.show();
});
addEvent(this._div, "mouseover", function() {
__this._visible = false;
});
addEvent(this._div, "mouseout", function() {
__this._visible = true;
});
addEvent(document, "mousedown", function() {
if (__this._visible) {
__this.hide();
}
});
// register global
window[this._uniqueId] = this;
},
getNode: function(id) {
return document.getElementById(id);
},
/*
toggle: function() {
return this._visible ? this.hide() : this.show();
},
*/
select: function(yyyyMMdd) {
this._selectedDate = new DateTime(yyyyMMdd.substr(0, 4), yyyyMMdd.substr(4, 2), yyyyMMdd.slice(6));
this._textNode.value = this._selectedDate.toString(this._config.selectedFormat);
this.update();
this.hide();
},
prevMonth: function() {
this._currentMonth = this._currentMonth.addMonths(-1);
this.update();
},
nextMonth: function() {
this._currentMonth = this._currentMonth.addMonths(1);
this.update();
},
show: function() {
this.ensureArea();
this._visible = true;
this.setDivStyle();
},
hide: function() {
this.ensureArea();
this._visible = false;
this.setDivStyle();
},
update: function() {
this.render();
},
render: function() {
var htmlBuilder = new StringBuilder();
this.renderHeader(htmlBuilder);
this.renderMonth(htmlBuilder);
var calendarContent = htmlBuilder.toString();
this._div.innerHTML = calendarContent;
},
renderHeader: function(htmlBuilder) {
htmlBuilder.appendFormat('<{0} class="{1}">', this._config.tags.header, "calendarHeader");
htmlBuilder.appendFormat('<{0}>', this._config.tags.headerChild);
// prev
var prevClick = String.format("window['{0}'].prevMonth();", this._uniqueId);
htmlBuilder.appendFormat('<{0} class="{1}"><a href="javascript:;" onclick="{2}"><span>&lt;</span></a></{0}>', this._config.tags.prev, "prev", prevClick);
// this month name
htmlBuilder.appendFormat('<{0} class="{1}">{2}</{0}>', this._config.tags.thisMonth, "thisMonth", this._currentMonth.toString(this._config.thisMonthFormat));
// next
var nextClick = String.format("window['{0}'].nextMonth();", this._uniqueId);
htmlBuilder.appendFormat('<{0} class="{1}"><a href="javascript:;" onclick="{2}"><span>&gt;</span></a></{0}>', this._config.tags.next, "next", nextClick);
htmlBuilder.appendFormat('</{0}>', this._config.tags.headerChild);
htmlBuilder.appendFormat('</{0}>', this._config.tags.header);
},
renderMonth: function(htmlBuilder) {
var renderDate = this.calcFirstDateOnCalendar();
htmlBuilder.appendFormat('<{0} class="calendar">', this._config.tags.month);
// render 7 week
for (var iWeek = 0; iWeek < 7; iWeek++) {
var isDay = iWeek != 0;
this.renderWeek(htmlBuilder, renderDate, isDay);
if (isDay) {
renderDate = renderDate.addDays(7);
}
}
htmlBuilder.appendFormat('</{0}>', this._config.tags.month);
},
renderWeek: function(htmlBuilder, renderDate, isDay) {
// render 1 week
htmlBuilder.appendFormat('<{0} class="{1}">', this._config.tags.week, !isDay ? "dayOfWeek" : "");
for (var iDayOfWeek = 0; iDayOfWeek < 7; iDayOfWeek++) {
if (isDay) {
this.renderDay(htmlBuilder, renderDate);
// next day
renderDate = renderDate.addDays(1);
} else {
this.renderDayOfWeek(htmlBuilder, iDayOfWeek);
}
}
htmlBuilder.appendFormat('</{0}>', this._config.tags.week);
},
renderDayOfWeek: function(htmlBuilder, dayOfWeek) {
var dayOfWeekName = this._config.dayOfWeekNames[dayOfWeek];
htmlBuilder.appendFormat('<{0}>{1}</{0}>', this._config.tags.dayOfWeek, dayOfWeekName);
},
renderDay: function(htmlBuilder, renderDate) {
var tdClass = [];
// dayOfWeek class
var dayOfWeek = renderDate.dayOfWeek();
switch (dayOfWeek) {
// sunday
case 0:
tdClass.push("sunday");
break;
// saturday
case 6:
tdClass.push("satuday");
break;
// other
default:
break;
}
// other month class
if (this._currentMonth.month() != renderDate.month()) {
tdClass.push("otherMonth");
}
// selected date class
if (this._selectedDate.equals(renderDate)) {
tdClass.push("selected");
}
// today class
if (this._today.equals(renderDate)) {
tdClass.push("today");
}
var noClick = false;
if (this._config.noClickDate) {
try {
var date = this._config.noClickDate;
var noClickDate = new DateTime(date.substr(0, 4), date.substr(4, 2), date.substr(6, 2));
noClick = noClickDate.compareTo(renderDate) >= 0;
} catch(e) {
}
}
var onclick = String.format("window['{0}'].select('{1}');", this._uniqueId, renderDate.toString("yyyyMMdd"));
if (noClick) {
onclick = "";
tdClass.push("noclick");
}
htmlBuilder.appendFormat(
'<{0} class="{1}" onclick="{2}">{3}</{0}>',
this._config.tags.day,
tdClass.join(' '),
onclick,
renderDate.day());
},
ensureArea: function() {
if(!this._div) {
this._div = document.createElement('div');
this._div.id = this._areaId;
this._div.className = 'popupCalendarArea';
document.body.appendChild(this._div);
}
},
calcFirstDateOnCalendar: function() {
var date = this._currentMonth;
var firstDayOnMonth = new DateTime(date.year(), date.month(), 1);
var firstDayOnMonthDayOfWeek = firstDayOnMonth.dayOfWeek();
return firstDayOnMonth.addDays(firstDayOnMonthDayOfWeek == 0 ? 0 : 0 - firstDayOnMonthDayOfWeek);
},
getElementScreenPosition: function(elm) {
//var html = document.documentElement;
var html = elm.ownerDocument.documentElement;
var rect = null;
if (elm.getBoundingClientRect) {
rect = elm.getBoundingClientRect();
} else if (document.getBoxObjectFor) {
pos = document.getBoxObjectFor(elm);
rect = { left: pos.x, top: pos.y };
} else {
return null;
}
var left = rect.left - (html.clientLeft ? html.clientLeft : 0);
var top = rect.top - (html.clientTop ? html.clientTop : 0);
return { "left": left, "top": top };
},
getPosition: function(elm){
var height = elm.offsetHeight;
// スクロール幅を取得
var html = document.documentElement;
var body = document.body;
var scrollLeft = (body.scrollLeft || html.scrollLeft);
var scrollTop = (body.scrollTop || html.scrollTop);
// 画面内座標を取得
var pos = this.getElementScreenPosition(elm);
// スクロール幅を加算
var left = pos.left + scrollLeft;
var top = pos.top + scrollTop;
return {"left" : left, "top" : top };
},
setDivStyle: function() {
var elm = this._triggerNode;
var pos = this.getPosition(elm);
this._div.style.position = 'absolute';
this._div.style.zIndex = 9999;
this._div.style.display = this._visible ? "" : "none";
this._div.style.top = pos.top + elm.offsetHeight;
this._div.style.left = pos.left;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment