script.js
var upcomingEventsFullCalendar = document.getElementsByClassName('upcoming-events')[0].getElementsByClassName('dates');
var calendar = {
create: function create(target, dates, events, showNextPrevDates) {
target[0].innerHTML = "";
if (showNextPrevDates) {
var prevMonthDays = calendar.months.previousMonthDays();
var nextMonthDays = calendar.months.nextMonthDays();
dates = prevMonthDays.concat(dates, nextMonthDays);
}
for (var i = 0; i < dates.length; i++) {
var currentItem = moment(dates[i]._d).format("YYYY-MM-DD");
var todaysEvents = [];
var date;
events.find(function(element, index, array) {
if (element.date === this) {
for (var i in element.events) {
todaysEvents.push(element.events[i]);
};
}
}, currentItem);
date = calendar.dates.buildDate(dates[i], todaysEvents);
$(target).parent().find(".calendar-header").html(moment(calendar.dates.today).format("MMMM, YYYY"));
$(target).append(date);
}
},
dates: {
buildDate: function buildDate(obj, events) {
var source = $("#date-template").html();
var template = Handlebars.compile(source);
var context = {};
var html;
var date = obj._d || moment(obj.date)._d;
if (moment(date).month() < moment(calendar.dates.today).month()) {
context.month = "prev-month";
} else if (moment(date).month() === moment(calendar.dates.today).month()) {
context.month = "";
} else if (moment(date).month() > moment(calendar.dates.today).month()) {
context.month = "next-month";
}
context.today = (moment(date).format("YYYY-MM-DD") ===
moment(calendar.dates.today).format("YYYY-MM-DD"));
context.dateNum = moment(date).format("D");
(events.length) ? context.hasEvents = true : context.hasEvents = false;
if (context.hasEvents) {
context.events = events;
}
html = template(context);
return html;
},
currentDate: function currentDate() {
return calendar.dates.today.getFullYear() +
'-' +
('0' + (calendar.dates.today.getMonth()+1)).slice(-2) +
'-' + ('0' + calendar.dates.today.getDate()).slice(-2);
},
today: new Date()
},
events: {
list: [],
getEvents: function getEvents(array) {
for (var i = 0; i < array.length; i++) {
var current = array[i];
$.ajax({
"type": "POST",
"url": current.url,
"data": current.data,
"success": function(data) {
var events = data[current.returnObj];
if (typeof events === "object") {
var convertedObj = $.map(events, function(values, index) {
for (var i in values) {
values[i].allDay = true
}
return {
date: index,
events: values
};
});
events = convertedObj;
}
for (var i in events) {
calendar.events.list.push(events[i]);
}
calendar.create(upcomingEventsFullCalendar,
calendar.months.currentMonthDays(),
calendar.events.list,
true);
}
});
}
}
},
months: {
currentMonthDays: function currentMonthDays() {
var arr = [];
for (var i = 1; i <= calendar.months.lastOfMonth().date(); i++) {
arr.push(moment().date(i));
}
return arr;
},
firstOfMonth: moment().date(1),
firstOfMonthWeekday: moment().date(1).weekday(),
lastOfMonth: function lastOfMonth() {
return moment().month(calendar.months.nextMonth).date(0);
},
nextMonth: moment().month() + 1,
nextMonthDays: function nextMonthDays() {
var arr = [];
for (var i = 1; i <= (7 - calendar.months.nextMonthStartDay()); i++) {
arr.push(moment().month(calendar.months.nextMonth).date(i));
}
return arr;
},
nextMonthStartDay: function nextMonthStartDay() {
return moment().month(calendar.months.nextMonth).date(1).weekday();
},
previousMonthDays: function previousMonthDays() {
var arr = [];
for (var i = 0; i < calendar.months.firstOfMonthWeekday; i++) {
arr.push(moment().date(1).weekday(i));
}
return arr;
},
prevMonthStartDay: moment().date(1).weekday(0)
}
}
var dates = calendar.months.currentMonthDays();
calendar.events.getEvents(
[
{
"name": "holidays",
"url": "http://holidayapi.com/v1/holidays",
"data": {
"country": "US",
"year": 2015
},
"returnObj": "holidays"
}
]
);
index.html
<section class="upcoming-events">
<header>
<h1 class="calendar-header">
</h1>
<ol class="days">
<li class="day" data-letter="S" data-abbr="Sun">Sunday</li>
<li class="day" data-letter="M" data-abbr="Mon">Monday</li>
<li class="day" data-letter="T" data-abbr="Tue">Tuesday</li>
<li class="day" data-letter="W" data-abbr="Wed">Wednesday</li>
<li class="day" data-letter="T" data-abbr="Thu">Thursday</li>
<li class="day" data-letter="F" data-abbr="Fri">Friday</li>
<li class="day" data-letter="S" data-abbr="Sat">Saturday</li>
</ol>
</header>
<ol class="dates">
</ol>
</section>
<script id="date-template" type="text/x-handlebars-template">
<li data-month="{{month}}" data-today="{{today}}" data-has-events="{{hasEvents}}">
<div class="details">
<span class="date">{{dateNum}}</span>
{{#if hasEvents}}
{{#each events as |event|}}
<span class="event" {{#if event.allDay}}data-all-day-event="{{event.allDay}}"{{/if}}>
{{event.name}}
</span>
{{/each}}
{{/if}}
</div>
</li>
</script>
Calendar Component (LESS, no framework).markdown
Calendar Component (LESS, no framework)
---------------------------------------
This is an example of how LESS can be used to create a semantically remapped calendar component.
A [Pen](http://codepen.io/dehuszar/pen/Yywgqj) by [dehuszar](http://codepen.io/dehuszar) on [CodePen](http://codepen.io/).
[License](http://codepen.io/dehuszar/pen/Yywgqj/license).
scripts
<script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/18728/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.0/handlebars.min.js"></script>
style.css
// layouts
@import (reference) "https://s3-us-west-2.amazonaws.com/s.cdpn.io/18728/calendar-agenda.less";
@import (reference) "https://s3-us-west-2.amazonaws.com/s.cdpn.io/18728/calendar-month-grid-full.less";
html {
font-family: 'PT Sans Narrow', sans-serif;
}
.upcoming-events {
.days li {
display: inline-block;
position: relative;
visibility: hidden;
&:after {
content: attr(data-letter);
display: inline-block;
left: 0;
position: absolute;
visibility: visible;
width: 100%;
}
@media (min-width: 800px) {
&:after {
content: attr(data-abbr);
}
}
@media (min-width: 960px) {
visibility: visible;
&:after {
content: ""
}
}
}
.dates li {
position: relative;
.details {
left: 0;
position: relative;
height: 100%;
top: 0;
width: 100%;
z-index: 1;
}
&:not([data-month="prev-month"]):not([data-month="next-month"]) .details {
background: rgba(255, 255, 255, 0.75);
}
&:after {
background: rgba(0, 0, 0, 0.25);
content: "";
position: absolute;
height: 100%;
left: 0;
top: 0;
width: 100%;
z-index: 0;
}
&[data-has-events="true"]:not([data-month="prev-month"]):not([data-month="next-month"]):after {
background: rgba(26, 146, 239, 0.75);
}
&[data-today="true"]:after {
background: rgba(255, 255, 255, 1)
}
}
// this agenda layout starts goes from 'mobile first' to 767px
&:extend(.agenda all);
// this grid layout div starts at 768px
&:extend(.month-grid-full all);
}