Last active
April 26, 2017 04:02
-
-
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
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
#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; | |
} |
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
/* | |
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