<!DOCTYPE HTML>
<meta charset="utf-8" />
<head>
<script src="[URL unfurl="true"]https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js"></script>[/URL]
<script>
Element.prototype.datepicker = function( option ){
var _this;
var options = {
ajaxServer: 'debug.php',
refreshInterval: 30000,
maskDays:[6,7],
alreadyTakenMessage: 'Sorry, that day is no longer available',
successMessage: 'Thank you, your click has been logged',
startMonth: -24,
endMonth: 24
}
var timer = {
start: function(){
timer.timer = window.setInterval(events.tick, im.getOption('refreshInterval'));
},
stop: function(){
window.clearInterval(timer.timer);
},
timer: ''
}
var ajaxRequest;
var ajax = {
r: '',
post: function(data, callback, async){
if(typeof async == 'undefined'){
async = true;
}
ajax.connect();
ajax.r.onreadystatechange = function(){
if(ajax.r.readyState == 4){
callback(JSON.parse(ajax.r.responseText));
}
}
ajax.r.open('POST', im.getOption('ajaxServer'), async);
ajax.r.setRequestHeader("Content-type","application/x-[URL unfurl="true"]www-form-urlencoded");[/URL]
ajax.r.send(im.serialize(data));
},
get: function(data, callback, async){
if(typeof async == 'undefined'){
async = true;
}
ajax.connect();
ajax.r.onreadystatechange = function(){
if(ajax.r.readyState == 4){
callback(JSON.parse(ajax.r.responseText));
}
}
ajax.r.open('GET', im.getOption('ajaxServer') + '?' + im.serialize(data), async);
ajax.r.send();
},
connect: function(){
try{
ajax.r = new XMLHttpRequest();
} catch (e){
try{
ajax.r = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajax.r = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
}
}
var events = {
dayClick: function(e){
var that = e.target;
if(!im.isAvailable(that)){
return;
}
im.addClick(that.dataset.year, that.dataset.month, that.dataset.day, that);
},
mouseover: function(e){
var that = e.target;
that.className = that.className + " hover";
that.addEventListener('mouseout', events.mouseleave);
},
mouseleave: function(e){
var that = e.target;
that.classList.remove('hover');
that.removeEventListener('mouseout', events.mouseover);
},
buttonClick: function(e){
var that = e.target;
that.preventDefault();
},
tick: function(){
im.refreshAvailability();
},
prevClick: function(e){
timer.stop();
e.preventDefault();
var cM = im.getCurrentMonth();
var m = new moment({year:cM.year, month:cM.month, day:1});
m.add(-1,'months');
methods.load( m.get('year'), m.get('month'));
},
nextClick: function(e){
timer.stop();
e.preventDefault();
var cM = im.getCurrentMonth();
var m = new moment({year:cM.year, month:cM.month, day:1});
m.add(1,'months');
methods.load(m.get('year'), m.get('month'));
},
selectChange: function(e){
var that = e.target;
var bits = that.value.split('-');
methods.load(bits[0],bits[1]);
}
}
var im = {
setOptions: function(opts){
for(var i in opts) options[i] = opts[i];
}
}
var methods = {
init: function(){
_this = this;
im.setOption('fontSize', parseFloat(window.getComputedStyle(_this,null).getPropertyValue('font-size')));
im.doSelect();
methods.load();
},
load: function(year, month, day ){
im.empty(_this);
container = im.getContainer();
var div = document.createElement('div');
div.classList.add('heading');
div.appendChild(im.getOption('prev'));
div.appendChild(im.getOption('select'));
div.appendChild(im.getOption('next'))
container.appendChild(div);
var days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
for (var i = 0; i < 7; i++) {
var cell = im.getBlankDay();
var t = document.createTextNode(days[i ]);
cell.appendChild(t);
cell.classList.remove('blankDay');
cell.classList.add(days[i].toLowerCase());
cell.classList.add('heading')
container.appendChild(cell);
}
if(year){
var m = new moment({year:year, month:month, day:1});
} else {
var m = new moment().set({date:1});
year = m.get('year');
month = m.get('month');
day = m.get('date');
}
var d = m.isoWeekday();
var startingMonth = m.month();
first = true;
while (m.month() == startingMonth) {
for (var i = 1; i <= 7; i++) {
if (i < d && first){
var cell = im.getBlankDay();
} else if(m.month() == startingMonth){
var cell = im.getDay(m.year(), m.month(), m.date());
m.add(1, 'days');
} else {
var cell = im.getBlankDay();
}
container.appendChild(cell);
}
first = false;
}
_this.appendChild(container);
im.setSelectValue(year,month);
im.refreshAvailability(year,month);
}
}
var im = { //internal only methods
serialize: function(obj){
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
},
s4: function(){
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
},
empty: function(elem){
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
},
guid: function(){
return im.s4() + im.s4() + '-' + im.s4() + '-' + im.s4() + '-' + im.s4() + '-' + im.s4() + im.s4() + im.s4();
},
setOption: function(option, value){
options[option] = value;
},
getOption: function(option){
return options[option] ? options[option] : null;
},
getContainer: function(){
var c = document.createElement('div');
c.className = "calendarContainer";
c.id = im.guid();
im.setOption('container', c.id);
var width = (im.convertToRem(im.getOption('fontSize')) * 17) + 'rem';
console.log(width);
c.style.width = width;
return c;
},
convertToRem: function(value){
console.log('value',value);
var aRem = im.convertFromRem(1);
var m = parseFloat(getComputedStyle(document.documentElement).fontSize);
console.log('conversion', value/m);
return value / m;
},
convertFromRem: function(value) {
var v = parseFloat(getComputedStyle(document.documentElement).fontSize);
return value * v;
},
getDay: function(year, month, day){
var m = new moment({
year: year,
month: month,
day: day
});
var d = document.createElement('span');
d.dataset.year = year;
d.dataset.month = month;
d.dataset.day = day;
d.dataset.blank = false;
d.classList.add("day");
d.classList.add(m.format('ddd').toLowerCase());
if (m.isSame(new Date(), "day")) {
d.classList.add('today');
}
d.appendChild(document.createTextNode(day));
d.addEventListener('click', events.dayClick);
d.addEventListener('mouseover', events.mouseover);
return d;
},
getBlankDay: function(){
var d = document.createElement('span');
d.text = ' ';
d.className = 'day blankDay';
d.dataset.blank = true;
return d;
},
addClick: function(year, month, day, elem){
timer.stop();
ajax.post({
action: 'addClick',
year: year,
month: month,
day: day
}, function(data){
if (data.result == 'ok') {
im.setAvailability(data);
alert(im.getOption('successMessage'));
}
else {
im.setAvailability(data);
alert(im.getOption('alreadyTakenMessage'));
}
}, false);
},
checkAvailability: function(year, month, elem){
im.refreshAvailability(year, month, true);
if (!im.isAvailable(elem)) {
alert('Too late. That date currently has no availability');
}
},
isAvailable: function(elem){
if (elem.dataset.clicks >= 3) {
return false;
}
else {
var m = new moment({
year: elem.dataset.year,
month: elem.dataset.month,
day: elem.dataset.day
});
if (im.getOption('maskDays').indexOf(m.isoWeekday()) > -1) {
return false;
}
else {
return true;
}
}
},
refreshAvailability: function(year, month){
timer.stop();
if (typeof year == 'undefined') {
var e = _this.querySelector('.day:not(.blankDay):not(.heading)');
year = e.dataset.year;
month = e.dataset.month;
}
ajax.post({
action: 'getAvailability',
year: year,
month: month
}, im.setAvailability, true);
},
setAvailability: function(obj){
if (obj.data) {
[].forEach.call(_this.querySelectorAll('.day:not(.blankDay):not(.heading)'), function(el){
el.dataset.clicks = obj.data[el.dataset.year][el.dataset.month][el.dataset.day];
var a = im.isAvailable(el);
if (a) {
el.classList.add('available');
el.classList.remove('unavailable');
}
else {
el.classList.add('unavailable');
el.classList.remove('available');
}
});
}
timer.start();
},
doSelect: function(){
var m = new moment().add(im.getOption('startMonth'), 'months');
var cM = im.getCurrentMonth();
var t = new moment({
year: cM.year,
month: cM.month,
day: cM.day
});
var s = document.createElement('select');
for (var i = 0; i < Math.abs(im.getOption('startMonth')) + im.getOption('endMonth'); i++) {
var opt = document.createElement('option');
opt.text = m.format('MMMM, YYYY');
opt.value = m.format('YYYY-MM');
if (m.isSame(t, 'month')) {
opt.selected = true;
}
m.add(1, 'months');
s.add(opt);
}
s.id = im.guid();
s.classList.add('selectBox');
s.addEventListener('change', events.selectChange);
var prev = document.createElement('a');
prev.classList.add('button');
prev.text = '<<';
prev.addEventListener('click', events.prevClick);
var next = document.createElement('a');
next.classList.add('button');
next.text = '>>';
next.addEventListener('click', events.nextClick);
im.setOption('select', s);
im.setOption('prev', prev);
im.setOption('next', next);
},
getCurrentMonth: function(){
var elem = _this.querySelector('.day:not(.blankDay):not(.heading)');
if (elem == null) {
var m = new moment();
return {
year: m.get('year'),
month: m.get('month')
}
}
return {
year: elem.dataset.year,
month: elem.dataset.month
}
},
setSelectValue: function(year, month){
month = month + 1;
if (month <= 9)
month = '0' + month;
var val = year + '-' + month;
var elem = _this.querySelector("select");
elem.value = val;
}
}
if(methods[option]){
return methods[option].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof option === 'object' || ! option ) {
return methods.init.apply( this, arguments );
} else {
return false;
}
}
</script>
<style>
#calendar{
font-size:0.8rem;
}
.calendarContainer {
padding-top: 1rem;
}
.calendarContainer .day {
box-sizing:border-box;
-moz-box-sizing:border-box;
-webkit-box-sizing: border-box;
font-size: inherit;
float: left;
width: calc((100%/7) - 0.25rem);
padding: 0;
margin: 0.125rem;
text-align: center;
border: 1px solid silver;
display:block;
vertical-align: middle;
}
.calendarContainer .today{
border-color: #5881fc;
}
.calendarContainer .blankDay{
border-color: transparent;
}
.calendarContainer, calendarContainer .sun {
clear: left;
}
.calendarContainer hover{
backgroundColor: #fbffd2;
}
.calendarContainer .available{
background-color: #b3ffd7;
cursor:pointer;
}
.calendarContainer .unavailable{
background-color: #ffccc9;
}
.calendarContainer .heading{
text-align:center;
margin-bottom: 0.5rem;
}
.calendarContainer .heading .button{
margin-left: 0.5rem;
margin-right: 0.5rem;
text-decoration: none;
cursor: pointer;
}
</style>
</head>
<body>
<div id="calendar"></div>
</body>
<script>
document.querySelector('#calendar').datepicker();
</script>