Skip to content

Instantly share code, notes, and snippets.

@vencil
Last active April 26, 2017 04:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vencil/0ad929aeb5a2d75a1bba240d1ed649c6 to your computer and use it in GitHub Desktop.
Save vencil/0ad929aeb5a2d75a1bba240d1ed649c6 to your computer and use it in GitHub Desktop.
A calendar component that can operates custom actions on your own date events
#EventCalendarToolbar {
width: 94%;
margin: 10px auto;
}
.EventCalendar_ToolGroup {
display: inline;
position: relative;
font-size: 0;
white-space: nowrap;
vertical-align: middle;
clear: left;
}
.EventCalendar_ToolGroup:nth-child(2) {
float: right;
}
.EventCalendar_btn {
font-size: 16px;
text-decoration: none;
padding: 5px 10px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: #FFFFFF -webkit-linear-gradient(top, #f5f5f5, #f1f1f1);
cursor: pointer;
}
.EventCalendar_btn:hover, .EventCalendar_btn .EventCalendar_btn_active {
color: #333333;
text-decoration: none;
background: #EFEFEF -webkit-linear-gradient(top, #e5e5e5, #e1e1e1);
}
.EventCalendar_title_txt {
font-size: 20px;
margin: 0 50px 0 15px;
display: inline-block;
}
.EventCalendar_Body table {
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
white-space: nowrap;
width: 94%;
margin: 0 auto;
box-sizing: border-box;
}
.EventCalendar_Body td {
border: 1px solid #ddd;
border-collapse: separate;
box-sizing: border-box;
vertical-align: top;
}
.EventCalendar_Header td {
text-align: center;
}
.EventCalendar_Corner_Cell {
width: 60px;
}
.EventCalendar_Day {
padding: 5px 0;
}
.EventCalendar_Day_Out_Focus {
color: grey;
}
.EventCalendar_Day_Today {
border: 1px solid black;
}
.EventCalendar_Day_Top_Bar {
font-size: 12px;
text-align: right;
padding-right: 2px;
}
.EventCalendar_Day_Events {
min-height: 60px;
}
.EventCalendar_DayTime_Indicator {
width: 60px;
padding: 0 4px;
vertical-align: middle;
text-align: right;
white-space: nowrap;
font-size: 1em;
}
.EventCalendar_Time {
height: 30px;
}
.EventCalendar_Time_All_Day{
min-height: 30px;
}
.EventCalendar_Time_Today {
background-color: #f1f1f1;
}
.EventCalendar_Evt_Day {
color: #777777;
background-color: #E4EFF8;
overflow-x: hidden;
cursor: pointer;
}
.EventCalendar_Evt_more{
color: cadetblue;
cursor: pointer;
}
.EventCalendar_Evt_more, .EventCalendar_Evt_Day, .EventCalendar_Evt_Filler {
padding: 1px 0 1px 5px;
margin-bottom: 3px;
line-height: 1.2;
white-space: nowrap;
font-size: 14px;
}
.EventCalendar_Evt_Hide {
display: none;
}
.EventCalendar_Evt_Time_All_Day {
color: #777777;
background-color: #E4EFF8;
margin-bottom: 2px;
overflow-x: hidden;
padding: 0 4px;
cursor: pointer;
font-size: 14px;
}
.EventCalendar_Time > table{
table-layout: fixed;
width: 85%;
margin: 0 5px 0 0;
}
.EventCalendar_Time td{
border:none;
position: relative; /*make z-index Div clickable */
}
.EventCalendar_Evt_Time {
position: absolute;
color: #777777;
border: 1px solid #9FC6E7;
box-sizing: border-box;
background-color: #E4EFF8;
white-space: pre-wrap;
word-wrap: break-word; /* IE */
padding: 0 4px;
cursor: pointer;
font-size: 14px;
width: 100%;
overflow: hidden;
}
/*
Copyright (c) 2016 by Vencil(vencsvencil@gmail.com), online demo:http://codepen.io/veni_vi/pen/VmRZJo/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Polyfill for IE
Number.isInteger = Number.isInteger || function(value) {
return typeof value === "number" &&
isFinite(value) &&
Math.floor(value) === value;
};
function cancelEventBubble(event) {
if (event.stopPropagation) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
}
}
var EventCalendar = (function() {
"use strict";
// Set up private variables
var focusDateTime = new Date(),
timeSplit = 0.5, // per o.5 hour as a time section.
time_view_init_time = 6,
time_view_end_time = 20,
time_div_height = 31, // same as the CSS setting.
am_suffix = "AM",
pm_suffix = "PM",
UNIT_MOMTH = "M",
UNIT_WEEK = "W",
UNIT_DAY = "D",
DIRECTION_NEXT = "N",
DIRECTION_PREV = "P",
focusTimeUnit = UNIT_MOMTH;
var calendar = {
view: null,
registeredActions: {},
title: document.createElement('DIV'),
events: {},
eventRenderFunc: null,
eventMoreFunc: null,
lang: {
today: 'Today',
month: 'month',
week: 'week',
day: 'day',
all_day: 'All day',
m0: 'January',
m1: 'February',
m2: 'March',
m3: 'April',
m4: 'May',
m5: 'June',
m6: 'July',
m7: 'August',
m8: 'September',
m9: 'October',
m10: 'November',
m11: 'December',
d0: 'Sunday',
d1: 'Monday',
d2: 'Tuesday',
d3: 'Wednesday',
d4: 'Thursday',
d5: 'Friday',
d6: 'Saturday'
}
};
calendar.loadView = function (view) {
this.view = view;
};
calendar.requireEventData = function (eventData) {
this.events = {}; // will classify by month
var i = 0, len = eventData.length;
for (; i < len; i++) {
var calendarEvent = new CalendarEvent(eventData[i]),
initDate = calendarEvent.date_init,
initDateProp = initDate.getFullYear() + "/" + initDate.getMonth();
if (!this.events.hasOwnProperty(initDateProp)) {
this.events[initDateProp] = [];
}
this.events[initDateProp].push(calendarEvent);
if (calendarEvent.date_end) {
var _init = new Date(calendarEvent.date_init.getFullYear(), calendarEvent.date_init.getMonth() + 1, 1),
_end = new Date(calendarEvent.date_end.getFullYear(), calendarEvent.date_end.getMonth(), 1);
while (_init <= _end) {
var _prop = _init.getFullYear() + "/" + _init.getMonth();
if (!this.events.hasOwnProperty(_prop)) {
this.events[_prop] = [];
}
this.events[_prop].push(calendarEvent);
_init.setMonth(_init.getMonth() + 1);
}
}
}
};
calendar.setEventRenderFunction = function (eventRenderFunc) {
this.eventRenderFunc = eventRenderFunc;
};
calendar.setEventMoreFunction = function (eventMoreFunc) {
this.eventMoreFunc = eventMoreFunc;
};
calendar.loadLanguage = function (lang) {
this.lang = lang;
};
calendar.loadConfig = function (config) {
if (config.hasOwnProperty("defaultView")) {
focusTimeUnit = config.defaultView;
}
if (config.hasOwnProperty("focusDateTime")) {
focusDateTime = config.focusDateTime;
}
if (config.hasOwnProperty("timeViewInitTime")) {
time_view_init_time = parseFloat(config.timeViewInitTime);
}
if (config.hasOwnProperty("timeViewEndTime")) {
time_view_end_time = parseFloat(config.timeViewEndTime);
}
if (config.hasOwnProperty("timeSplit")) {
timeSplit = parseFloat(config.timeSplit);
}
};
calendar.registerAction = function (domId, event, action) {
var _action = {'event': event, 'action': action};
if (this.registeredActions.hasOwnProperty(domId)) {
this.registeredActions.domId.push(_action);
} else {
this.registeredActions[domId] = [];
this.registeredActions[domId].push(_action);
}
};
calendar.render = function () {
var calendarBox = document.createElement('DIV'),
calendarToolbar = document.createElement('DIV');
calendarBox.appendChild(calendarToolbar);
if (!this.view) {
if (focusTimeUnit == UNIT_MOMTH) {
this.loadView(CALENDAR_MONTH_VIEW);
} else if (focusTimeUnit == UNIT_WEEK) {
this.loadView(CALENDAR_WEEK_VIEW);
} else if (focusTimeUnit == UNIT_DAY) {
this.loadView(CALENDAR_DAY_VIEW);
}
}
// draw toolbar
calendarToolbar.id = "EventCalendarToolbar";
var leftTool = document.createElement('DIV'), rightTool = document.createElement('DIV'),
prevBtn = document.createElement('BUTTON'),
todayBtn = document.createElement('BUTTON'),
nextBtn = document.createElement('BUTTON'),
monthViewBtn = document.createElement('BUTTON'),
weekViewBtn = document.createElement('BUTTON'),
dayViewBtn = document.createElement('BUTTON');
this.title = document.createElement('DIV');
leftTool.classList.add("EventCalendar_ToolGroup");
rightTool.classList.add("EventCalendar_ToolGroup");
rightTool.classList.add("printHide");
leftTool.appendChild(prevBtn);
prevBtn.appendChild(document.createTextNode("<"));
prevBtn.classList.add("EventCalendar_btn");
prevBtn.classList.add("printHide");
prevBtn.dataset.direction = DIRECTION_PREV;
leftTool.appendChild(todayBtn);
todayBtn.appendChild(document.createTextNode(this.lang['today']));
todayBtn.classList.add("EventCalendar_btn");
todayBtn.classList.add("printHide");
leftTool.appendChild(nextBtn);
nextBtn.appendChild(document.createTextNode(">"));
nextBtn.classList.add("EventCalendar_btn");
nextBtn.classList.add("printHide");
nextBtn.dataset.direction = DIRECTION_NEXT;
leftTool.appendChild(this.title);
this.title.classList.add("EventCalendar_title_txt");
prevBtn.addEventListener('click', this.changeDateTime);
nextBtn.addEventListener('click', this.changeDateTime);
todayBtn.addEventListener('click', this.findToday);
rightTool.appendChild(monthViewBtn);
monthViewBtn.appendChild(document.createTextNode(this.lang['month']));
monthViewBtn.classList.add("EventCalendar_btn");
monthViewBtn.dataset.timeUnit = UNIT_MOMTH;
monthViewBtn.id = "EventCalendar_Btn_Month";
rightTool.appendChild(weekViewBtn);
weekViewBtn.appendChild(document.createTextNode(this.lang['week']));
weekViewBtn.classList.add("EventCalendar_btn");
weekViewBtn.dataset.timeUnit = UNIT_WEEK;
weekViewBtn.id = "EventCalendar_Btn_Week";
rightTool.appendChild(dayViewBtn);
dayViewBtn.appendChild(document.createTextNode(this.lang['day']));
dayViewBtn.classList.add("EventCalendar_btn");
dayViewBtn.dataset.timeUnit = UNIT_DAY;
dayViewBtn.id = "EventCalendar_Btn_Day";
monthViewBtn.addEventListener('click', this.changeView);
weekViewBtn.addEventListener('click', this.changeView);
dayViewBtn.addEventListener('click', this.changeView);
// draw main view
var calendarBody = this.view.render();
// assembling virtual DOM component here to reduce view reflow.
calendarBox.appendChild(calendarBody);
calendarToolbar.appendChild(leftTool);
calendarToolbar.appendChild(rightTool);
// rebind events to toolbar
var _domId;
for (_domId in this.registeredActions) {
if (this.registeredActions.hasOwnProperty(_domId)) {
var qo = calendarToolbar.querySelector('#' + _domId);
if (qo) {
var _actions = this.registeredActions[_domId], _action, i, len;
for (i = 0, len = _actions.length; i < len; i++) {
_action = _actions[i];
qo.addEventListener(_action.event, _action.action);
}
}
}
}
return calendarBox;
};
calendar.updateTitle = function (text) {
this.title.textContent = text;
};
calendar.changeDateTime = function (event) {
cancelEventBubble(event);
var direction = this.dataset.direction;
if (direction == DIRECTION_PREV) {
if (focusTimeUnit == UNIT_MOMTH) {
focusDateTime.setMonth(focusDateTime.getMonth() - 1);
} else if (focusTimeUnit == UNIT_WEEK) {
focusDateTime.setDate(focusDateTime.getDate() - 7);
} else if (focusTimeUnit == UNIT_DAY) {
focusDateTime.setDate(focusDateTime.getDate() - 1);
}
} else if (direction == DIRECTION_NEXT) {
if (focusTimeUnit == UNIT_MOMTH) {
focusDateTime.setMonth(focusDateTime.getMonth() + 1);
} else if (focusTimeUnit == UNIT_WEEK) {
focusDateTime.setDate(focusDateTime.getDate() + 7);
} else if (focusTimeUnit == UNIT_DAY) {
focusDateTime.setDate(focusDateTime.getDate() + 1);
}
}
calendar.view.render();
};
calendar.changeView = function (event) {
cancelEventBubble(event);
focusTimeUnit = this.dataset.timeUnit;
if (focusTimeUnit == UNIT_MOMTH) {
calendar.loadView(CALENDAR_MONTH_VIEW);
} else if (focusTimeUnit == UNIT_WEEK) {
calendar.loadView(CALENDAR_WEEK_VIEW);
} else if (focusTimeUnit == UNIT_DAY) {
calendar.loadView(CALENDAR_DAY_VIEW);
}
calendar.view.render();
};
calendar.findToday = function (event) {
cancelEventBubble(event);
focusDateTime = new Date();
calendar.view.render();
};
/**
* Base class of calendar view
* @constructor
*/
function CalendarView() {
}
CalendarView.prototype.render = function () {
var calendarWrapper = document.createElement("DIV"),
calendarHeader = document.createElement("TABLE"),
calendarContent = document.createElement("TABLE");
this.drawHeader(calendarHeader);
this.drawTimeSection(calendarContent);
calendarWrapper.appendChild(calendarHeader);
calendarWrapper.appendChild(calendarContent);
calendarWrapper.classList.add("EventCalendar_Body");
calendarHeader.classList.add("EventCalendar_Header");
if (document.querySelector(".EventCalendar_Body")) {
var oldNode = document.querySelector(".EventCalendar_Body");
oldNode.parentNode.replaceChild(calendarWrapper, oldNode);
}
calendar.updateTitle(this.createTitle());
this.drawEvents(calendarContent);
// rebind events to calendar
var _domId;
for (_domId in calendar.registeredActions) {
if (calendar.registeredActions.hasOwnProperty(_domId)) {
var qo = calendarWrapper.querySelector('#' + _domId);
if (qo) {
var _actions = this.registeredActions[_domId], _action, i, len;
for (i = 0, len = _actions.length; i < len; i++) {
_action = _actions[i];
qo.addEventListener(_action.event, _action.action);
}
}
}
}
return calendarWrapper;
};
function CalendarMonthView() {
CalendarView.call(this);
this.drawHeader = function (table) {
var headerRow = table.insertRow(-1), cell;
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d0));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d1));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d2));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d3));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d4));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d5));
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(calendar.lang.d6));
};
this.drawTimeSection = function (contentTable) {
var row,
firstDay = new Date(focusDateTime.getFullYear(), focusDateTime.getMonth(), 1),
lastDay = new Date(focusDateTime.getFullYear(), focusDateTime.getMonth() + 1, 0),
operatorDate = new Date(focusDateTime.getFullYear(), focusDateTime.getMonth(), 1),
currentDate = 1,
lastDate = lastDay.getDate(),
_day;
while (currentDate <= lastDate) {
operatorDate.setDate(currentDate);
_day = operatorDate.getDay();
if (currentDate == 1 || _day == 0) {
row = contentTable.insertRow(-1);
var i = 0;
for (; i < 7; i++) {
row.insertCell(-1);
}
}
row.cells[_day].appendChild(addCalendarDay(operatorDate));
currentDate++;
}
// fill the rest cells with days from other months.
if (firstDay.getDay() > 0) {
row = contentTable.rows[0];
operatorDate = new Date(focusDateTime.getFullYear(), focusDateTime.getMonth(), 0);
do {
_day = operatorDate.getDay();
row.cells[_day].appendChild(addCalendarDay(operatorDate));
operatorDate.setDate(operatorDate.getDate() - 1);
} while (_day > 0)
}
if (lastDay.getDay() < 6) {
row = contentTable.rows[contentTable.rows.length - 1];
operatorDate = new Date(focusDateTime.getFullYear(), focusDateTime.getMonth() + 1, 1);
while ((_day = operatorDate.getDay()) > 0) {
row.cells[_day].appendChild(addCalendarDay(operatorDate));
operatorDate.setDate(operatorDate.getDate() + 1);
}
}
};
this.createTitle = function () {
return calendar.lang["m" + focusDateTime.getMonth()] + " " + focusDateTime.getFullYear();
};
this.drawEvents = function (calendarContent) {
var rowSize = calendarContent.rows.length,
cellSize = calendarContent.rows[0].cells.length,
i, j, k, row, cell, _date, _dateEvents, classifyProp,
moreOptionsLimit = 3, meetLimit, eventBox, eventDiv,
orderEventDivArray, orderEventDivArrayLen, longEventIndexRecord, fillIndex, indexToBeDel,
firstCell = calendarContent.rows[0].cells[0];
for (i = 0; i < rowSize; i++) {
row = calendarContent.rows[i];
longEventIndexRecord = {};
for (j = 0; j < cellSize; j++) {
cell = row.cells[j];
_date = new Date(parseInt(cell.firstChild.dataset.date));
classifyProp = _date.getFullYear() + "/" + _date.getMonth();
if (calendar.events.hasOwnProperty(classifyProp)) {
_dateEvents = calendar.events[classifyProp].filter(function (evt) {
return evt.isEventDate(_date);
});
if (_dateEvents.length) {
eventBox = cell.querySelector(".EventCalendar_Day_Events");
orderEventDivArray = [];
indexToBeDel = [];
_dateEvents.forEach(function (evt) {
eventDiv = evt.render();
eventDiv.classList.add("EventCalendar_Evt_Day");
if (!evt.is_one_day_event && _date.toDateString() !== evt.date_init.toDateString() && cell !== firstCell) {
eventDiv.appendChild(document.createTextNode('\u00A0'));
} else {
eventDiv.appendChild(document.createTextNode(evt.createContentText()));
if (!evt.is_one_day_event) {
eventDiv.style.overflowX = "visible";
}
}
var found = false;
if (!evt.is_one_day_event) {
var _index, recordedId;
for (_index in longEventIndexRecord) {
recordedId = longEventIndexRecord[_index];
if (recordedId === evt.eventId) {
found = true;
fillIndex = parseInt(_index);
break;
}
}
}
if (!found) {
fillIndex = 0;
while (longEventIndexRecord.hasOwnProperty(fillIndex + "")) {
fillIndex++;
}
}
if (!evt.is_one_day_event) {
if (evt.date_end.getDate() === _date.getDate() &&
evt.date_end.getMonth() === _date.getMonth() &&
evt.date_end.getFullYear() === _date.getFullYear()) {
indexToBeDel.push(fillIndex);
} else {
longEventIndexRecord[fillIndex + ""] = evt.eventId;
}
} else {
longEventIndexRecord[fillIndex + ""] = evt.eventId;
indexToBeDel.push(fillIndex);
}
orderEventDivArray[fillIndex] = eventDiv;
});
indexToBeDel.forEach(function (delIndex) {
delete longEventIndexRecord[delIndex + ""];
});
meetLimit = orderEventDivArray.length > moreOptionsLimit;
for (k = 0, orderEventDivArrayLen = orderEventDivArray.length; k < orderEventDivArrayLen; k++) {
eventDiv = orderEventDivArray[k];
if (!eventDiv) {
eventDiv = document.createElement('DIV');
eventDiv.appendChild(document.createTextNode('\u00A0'));
eventDiv.classList.add("EventCalendar_Evt_Filler");
}
eventBox.appendChild(eventDiv);
if (meetLimit && k + 1 >= moreOptionsLimit) {
eventDiv.classList.add("EventCalendar_Evt_Hide");
}
}
if (meetLimit) {
eventBox.appendChild(createAddMore(_date));
}
}
}
}
}
};
/**
* @param date
* @returns {Element} to describe day and day events.
*/
function addCalendarDay(date) {
var contentBox = document.createElement('DIV'),
topBar = document.createElement('DIV'),
eventContent = document.createElement('DIV');
contentBox.classList.add("EventCalendar_Day");
topBar.classList.add("EventCalendar_Day_Top_Bar");
eventContent.classList.add("EventCalendar_Day_Events");
contentBox.dataset.date = date.getTime();
contentBox.appendChild(topBar);
contentBox.appendChild(eventContent);
if (date.getMonth() !== focusDateTime.getMonth()) {
contentBox.classList.add("EventCalendar_Day_Out_Focus");
}
var now = new Date();
if (date.getDate() === now.getDate() &&
date.getMonth() === now.getMonth() &&
date.getFullYear() === now.getFullYear()) {
contentBox.classList.add("EventCalendar_Day_Today");
}
contentBox.id = "EventCalendar" + date.getMonth() + date.getDate();
topBar.appendChild(document.createTextNode(date.getDate()));
return contentBox;
}
function createAddMore(dateTime) {
var moreOption = document.createElement('DIV');
moreOption.appendChild(document.createTextNode('more'));
moreOption.classList.add("EventCalendar_Evt_more");
moreOption.classList.add("printHide");
moreOption.dataset.date = dateTime.getTime();
moreOption.addEventListener('click', showMoreEvents);
return moreOption;
}
/**
* Call custom function to display all CalendarEvents in a chosen date.
* @param event
*/
function showMoreEvents(event) {
cancelEventBubble(event);
var date_more = new Date(parseInt(this.dataset.date)),
dateProp = date_more.getFullYear() + "/" + date_more.getMonth();
if (calendar.events.hasOwnProperty(dateProp) && calendar.eventMoreFunc) {
var _monthEvents = calendar.events[dateProp];
calendar.eventMoreFunc(_monthEvents.filter(function (evt) {
return evt.isEventDate(date_more);
}));
}
}
}
CalendarMonthView.prototype = Object.create(CalendarView.prototype);
CalendarMonthView.prototype.constructor = CalendarMonthView;
var CALENDAR_MONTH_VIEW = new CalendarMonthView();
function CalendarWeekView() {
CalendarView.call(this);
this.drawHeader = function (table) {
var headerRow = table.insertRow(-1),
cell = headerRow.insertCell(-1);
cell.classList.add('EventCalendar_Corner_Cell');
var firstDay = focusDateTime.getDate() - focusDateTime.getDay(),
adder = 0;
for (; adder < 7; adder++) {
var day = new Date(focusDateTime.getTime());
day.setDate(firstDay + adder);
cell = headerRow.insertCell(-1);
cell.appendChild(document.createTextNode(
(day.getMonth() + 1) + '/' + day.getDate() + " " + calendar.lang[('d' + adder)]));
}
};
this.drawTimeSection = function (contentTable) {
var rowIndex = 0, timeSectionSize = (time_view_end_time - time_view_init_time) / timeSplit,
operateTime = time_view_init_time, usedSuffix = am_suffix, row, cell, timeDiv,
now = new Date(), _time,
firstDay = focusDateTime.getDate() - focusDateTime.getDay(), dayIndex;
// draw all day sections
row = contentTable.insertRow(-1);
// draw all day section left side unit indicator
cell = row.insertCell(-1);
cell.classList.add("EventCalendar_DayTime_Indicator");
cell.appendChild(document.createTextNode(calendar.lang.all_day));
// draw rest all day section
for (dayIndex = 0; dayIndex < 7; dayIndex++) {
cell = row.insertCell(-1);
timeDiv = document.createElement('DIV');
cell.appendChild(timeDiv);
_time = new Date(focusDateTime.getTime()); // setDate may cross the month, so reset _time is required.
_time.setDate(firstDay + dayIndex);
timeDiv.classList.add("EventCalendar_Time_All_Day");
timeDiv.dataset.date = _time.getTime();
timeDiv.id = "EventCalendar" + _time.getMonth() + _time.getDate();
if (_time.getDate() === now.getDate() &&
_time.getMonth() === now.getMonth() &&
_time.getFullYear() === now.getFullYear()) {
cell.classList.add("EventCalendar_Time_Today");
}
}
for (; rowIndex < timeSectionSize; rowIndex++) {
// draw left side unit indicator
row = contentTable.insertRow(-1);
cell = row.insertCell(-1);
cell.classList.add("EventCalendar_DayTime_Indicator");
if (Number.isInteger(operateTime)) {
cell.appendChild(document.createTextNode(
parseInt(operateTime > 12 ? operateTime - 12 : operateTime) + usedSuffix));
} else {
cell.appendChild(document.createTextNode('\u00A0'));
}
// draw data table
for (dayIndex = 0; dayIndex < 7; dayIndex++) {
cell = row.insertCell(-1);
timeDiv = document.createElement('DIV');
cell.appendChild(timeDiv);
_time = new Date(focusDateTime.getTime());
_time.setDate(firstDay + dayIndex);
_time.setHours(Math.floor(operateTime));
_time.setMinutes(60 * (operateTime % 1));
timeDiv.classList.add("EventCalendar_Time");
timeDiv.dataset.date = _time.getTime();
timeDiv.id = "EventCalendar" + _time.getMonth() + _time.getDate() +
_time.getHours() + _time.getMinutes();
if (_time.getDate() === now.getDate() && _time.getMonth() === now.getMonth() &&
_time.getFullYear() === now.getFullYear()) {
cell.classList.add("EventCalendar_Time_Today");
}
}
operateTime += timeSplit;
if (operateTime > 12) usedSuffix = pm_suffix;
}
};
this.createTitle = function () {
var firstDay = focusDateTime.getDate() - focusDateTime.getDay(),
lastDay = firstDay + 6,
_firstDate = new Date(focusDateTime.getTime()),
_lastDate = new Date(focusDateTime.getTime());
_firstDate.setDate(firstDay);
_lastDate.setDate(lastDay);
return focusDateTime.getFullYear() + " " +
calendar.lang["m" + _firstDate.getMonth()] + _firstDate.getDate() + " — " +
(_firstDate.getMonth() == _lastDate.getMonth() ? "" : calendar.lang["m" + _lastDate.getMonth()]) +
_lastDate.getDate();
};
this.drawEvents = function (calendarContent) {
var firstDay = focusDateTime.getDate() - focusDateTime.getDay(),
lastDay = firstDay + 6,
_date = new Date(focusDateTime.getTime()),
_lastDate = new Date(focusDateTime.getTime()),
classifyProp, _dateEvents;
_date.setDate(firstDay);
_date.setHours(0, 0, 0, 0);
_lastDate.setDate(lastDay + 1);
_lastDate.setHours(0, 0, 0, 0);
while (_date.getTime() < _lastDate.getTime()) {
classifyProp = _date.getFullYear() + "/" + _date.getMonth();
if (calendar.events.hasOwnProperty(classifyProp)) {
_dateEvents = calendar.events[classifyProp].filter(function (evt) {
return evt.isEventDate(_date);
});
_dateEvents.forEach(function (evt) {
if (!evt.is_one_day_event || (evt.is_one_day_event && !evt.date_end) ||
(evt.date_end && evt.date_init.getHours() < time_view_init_time &&
(evt.date_end.getHours() + evt.date_end.getMinutes() / 60) > time_view_end_time)) {
// all day event
var dayEvent = evt.render(),
_dayText = evt.date_end ? evt.content : evt.createContentText(),
allDayContainer = calendarContent.rows[0].cells[_date.getDay() + 1].firstChild;
if (allDayContainer) {
allDayContainer.appendChild(dayEvent);
dayEvent.appendChild(document.createTextNode(_dayText));
dayEvent.classList.add('EventCalendar_Evt_Time_All_Day');
}
} else {
var eventInitTimeSection = Math.max(evt.date_init.getHours() + evt.date_init.getMinutes() / 60, time_view_init_time),
eventEndTimeSection = Math.min(evt.date_end.getHours() + evt.date_end.getMinutes() / 60, time_view_end_time),
timeEvent = evt.render(),
crossHeight = (eventEndTimeSection - eventInitTimeSection) / timeSplit * time_div_height,
container = calendarContent.querySelector("#EventCalendar" + evt.date_init.getMonth() +
evt.date_init.getDate() + parseInt(eventInitTimeSection) + evt.date_init.getMinutes());
if (container) {
var eventTable = container.querySelector('.EventCalendar_Evt_Time_Table');
if (!eventTable) {
eventTable = document.createElement('TABLE');
eventTable.classList.add('EventCalendar_Evt_Time_Table');
container.appendChild(eventTable);
eventTable.insertRow(-1);
}
if (eventTable.rows[0].cells.length === 1) {
var firstDiv = eventTable.rows[0].cells[0].querySelector('DIV');
firstDiv.style.width = "";
}
timeEvent.style.height = crossHeight + "px";
timeEvent.style.zIndex = eventInitTimeSection * 2;
if (eventTable.rows[0].cells.length === 0) {
timeEvent.style.width = "65%";
}
timeEvent.appendChild(document.createTextNode(evt.createContentText()));
timeEvent.classList.add('EventCalendar_Evt_Time');
var cell = eventTable.rows[0].insertCell(-1);
cell.appendChild(timeEvent);
}
}
});
}
_date.setDate(_date.getDate() + 1);
}
};
}
CalendarWeekView.prototype = Object.create(CalendarView.prototype);
CalendarWeekView.prototype.constructor = CalendarWeekView;
var CALENDAR_WEEK_VIEW = new CalendarWeekView();
function CalendarDayView() {
CalendarView.call(this);
this.drawHeader = function (table) {
var headerRow = table.insertRow(-1),
firstCell = headerRow.insertCell(-1),
DayCell = headerRow.insertCell(-1);
firstCell.classList.add('EventCalendar_Corner_Cell');
DayCell.appendChild(document.createTextNode((focusDateTime.getMonth() + 1) + "/" + focusDateTime.getDate() +
" " + calendar.lang[('d' + focusDateTime.getDay())]));
};
this.drawTimeSection = function (contentTable) {
var rowIndex = 0, timeSectionSize = (time_view_end_time - time_view_init_time) / timeSplit,
operateTime = time_view_init_time, usedSuffix = am_suffix, row, cell, timeDiv,
now = new Date(), _time = new Date(focusDateTime.getTime());
var isToday = false;
if (_time.getDate() === now.getDate() && _time.getMonth() === now.getMonth() &&
_time.getFullYear() === now.getFullYear()) {
isToday = true;
}
// draw all day sections
row = contentTable.insertRow(-1);
// draw all day section left side unit indicator
cell = row.insertCell(-1);
cell.classList.add("EventCalendar_DayTime_Indicator");
cell.appendChild(document.createTextNode(calendar.lang.all_day));
// draw rest all day section
cell = row.insertCell(-1);
timeDiv = document.createElement('DIV');
cell.appendChild(timeDiv);
timeDiv.classList.add("EventCalendar_Time_All_Day");
timeDiv.dataset.date = _time.getTime();
timeDiv.id = "EventCalendar" + _time.getMonth() + _time.getDate();
if (isToday) {
cell.classList.add("EventCalendar_Time_Today");
}
for (; rowIndex < timeSectionSize; rowIndex++) {
// draw left side unit indicator
row = contentTable.insertRow(-1);
cell = row.insertCell(-1);
cell.classList.add("EventCalendar_DayTime_Indicator");
if (Number.isInteger(operateTime)) {
cell.appendChild(document.createTextNode(
parseInt(operateTime > 12 ? operateTime - 12 : operateTime) + usedSuffix));
} else {
cell.appendChild(document.createTextNode('\u00A0'));
}
// draw data cell
cell = row.insertCell(-1);
timeDiv = document.createElement('DIV');
cell.appendChild(timeDiv);
timeDiv.classList.add("EventCalendar_Time");
if (isToday) {
cell.classList.add("EventCalendar_Time_Today");
}
_time.setHours(Math.floor(operateTime));
_time.setMinutes(60 * (operateTime % 1));
timeDiv.dataset.date = _time.getTime();
timeDiv.id = "EventCalendar" + _time.getMonth() + _time.getDate() +
_time.getHours() + _time.getMinutes();
operateTime += timeSplit;
if (operateTime > 12) usedSuffix = pm_suffix;
}
};
this.createTitle = function () {
return focusDateTime.getFullYear() + "/" + (focusDateTime.getMonth() + 1) + "/" + focusDateTime.getDate() +
" (" + calendar.lang[('d' + focusDateTime.getDay())] + ")";
};
this.drawEvents = function (calendarContent) {
var _date = new Date(focusDateTime.getTime()), classifyProp, _dateEvents;
_date.setHours(0, 0, 0, 0);
classifyProp = _date.getFullYear() + "/" + _date.getMonth();
if (calendar.events.hasOwnProperty(classifyProp)) {
_dateEvents = calendar.events[classifyProp].filter(function (evt) {
return evt.isEventDate(_date);
});
_dateEvents.forEach(function (evt) {
if (!evt.is_one_day_event || (evt.is_one_day_event && !evt.date_end) ||
(evt.date_end && evt.date_init.getHours() < time_view_init_time &&
(evt.date_end.getHours() + evt.date_end.getMinutes() / 60) > time_view_end_time)) {
// all day event
var dayEvent = evt.render(),
_dayText = evt.date_end ? evt.content : evt.createContentText(),
allDayContainer = calendarContent.rows[0].cells[1].firstChild;
if (allDayContainer) {
allDayContainer.appendChild(dayEvent);
dayEvent.appendChild(document.createTextNode(_dayText));
dayEvent.classList.add('EventCalendar_Evt_Time_All_Day');
}
} else {
var eventInitTimeSection = Math.max(evt.date_init.getHours() + evt.date_init.getMinutes() / 60, time_view_init_time),
eventEndTimeSection = Math.min(evt.date_end.getHours() + evt.date_end.getMinutes() / 60, time_view_end_time),
timeEvent = evt.render(),
crossHeight = (eventEndTimeSection - eventInitTimeSection) / timeSplit * time_div_height,
container = calendarContent.querySelector("#EventCalendar" + evt.date_init.getMonth() +
evt.date_init.getDate() + parseInt(eventInitTimeSection) + evt.date_init.getMinutes());
if (container) {
var eventTable = container.querySelector('.EventCalendar_Evt_Time_Table');
if (!eventTable) {
eventTable = document.createElement('TABLE');
eventTable.classList.add('EventCalendar_Evt_Time_Table');
container.appendChild(eventTable);
eventTable.insertRow(-1);
}
if (eventTable.rows[0].cells.length === 1) {
var firstDiv = eventTable.rows[0].cells[0].querySelector('DIV');
firstDiv.style.width = "";
}
timeEvent.style.height = crossHeight + "px";
timeEvent.style.zIndex = eventInitTimeSection * 2;
if (eventTable.rows[0].cells.length === 0) {
timeEvent.style.width = "65%";
}
timeEvent.appendChild(document.createTextNode(evt.createContentText()));
timeEvent.classList.add('EventCalendar_Evt_Time');
var cell = eventTable.rows[0].insertCell(-1);
cell.appendChild(timeEvent);
}
}
});
}
};
}
CalendarDayView.prototype = Object.create(CalendarView.prototype);
CalendarDayView.prototype.constructor = CalendarDayView;
var CALENDAR_DAY_VIEW = new CalendarDayView();
var _eventId = 0;
function CalendarEvent(arg) {
this.content = typeof arg.content === 'string' && arg.content.length ? arg.content : '\u00A0';
this.date_init = new Date(arg.date_init);
this.date_init_format_date_only = typeof arg.date_init === 'string' && arg.date_init.indexOf(":") === -1;
this.date_end = arg.date_end ? new Date(arg.date_end) : null;
this.date_end_format_date_only = false;
this.is_one_day_event = true;
if (this.date_end) {
if (this.date_end.getTime() <= this.date_init.getTime()) {// invalid end date
this.date_end = null;
} else {
this.date_end_format_date_only = typeof arg.date_end === 'string' && arg.date_end.indexOf(":") === -1;
this.is_one_day_event = this.date_end.toDateString() === this.date_init.toDateString();
}
}
this.eventId = _eventId++;
// TODO add your custom data here.
}
CalendarEvent.prototype.render = function () {
var contentBox = document.createElement('DIV');
if (calendar.eventRenderFunc) {
calendar.eventRenderFunc(contentBox, this);
}
return contentBox;
};
CalendarEvent.prototype.isEventDate = function (datetime, compareTimePart) {
var _init = this.date_init, _end = this.date_end;
if (compareTimePart) {
if (this.date_end) {
if (this.date_end_format_date_only) {
_end = new Date(this.date_end.getTime());
_end.setDate(_end.getDate() + 1);
_end.setHours(0, 0, 0, 0);
return datetime.getTime() >= _init.getTime() &&
datetime.getTime() < _end.getTime();
} else {
return datetime.getTime() >= _init.getTime() &&
datetime.getTime() <= _end.getTime();
}
} else {
return datetime.getTime() === _init.getTime();
}
} else {
datetime.setHours(0, 0, 0, 0);
if (_end) {
if (!this.date_init_format_date_only || !this.date_end_format_date_only) {
_init = new Date(this.date_init.getTime());
_end = new Date(this.date_end.getTime());
_init.setHours(0, 0, 0, 0);
_end.setHours(0, 0, 0, 0);
}
return datetime.getTime() >= _init.getTime() &&
datetime.getTime() <= _end.getTime();
} else {
if (!this.date_init_format_date_only) {
_init = new Date(this.date_init.getTime());
_init.setHours(0, 0, 0, 0);
}
return datetime.getTime() === _init.getTime();
}
}
};
CalendarEvent.prototype.createContentText = function () {
var initHours = this.date_init.getHours(),
minutes = this.date_init.getMinutes();
if (initHours > 0) {
var timeText;
if (initHours > 12) {
timeText = pm_suffix + (initHours - 12);
} else {
timeText = am_suffix + initHours;
}
timeText += ':' + (minutes < 10 ? '0' + minutes : minutes);
return '(' + timeText + ') ' + this.content;
} else {
return this.content;
}
};
return calendar;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment