Skip to content

Instantly share code, notes, and snippets.

@coma
Created October 10, 2013 08:39
Show Gist options
  • Save coma/6915057 to your computer and use it in GitHub Desktop.
Save coma/6915057 to your computer and use it in GitHub Desktop.
(function($, _, undefined) {
var resetTime = function(m) {
return m.hour(0).minute(0).second(0);
};
$.fn.CalendarWidget = function(options) {
var pluginName = 'CalendarWidget';
var plugin = $(this).data(pluginName);
if(plugin) {
return plugin;
}
return this.each(function() {
var defaults = {
format: {
main: 'YYYY-MM-DD',
month: 'MMMM YYYY'
},
min: _(),
max: _().add('y', 1),
container: 'body',
style: {
main: 'calendar'
},
tolerance: 100,
label: {
cancel: 'Cancel',
set : 'Set',
title : ''
}
};
var input = $(this);
var data = input.data('widget-calendar');
var settings = $.extend(defaults, data, options);
var widget = $('<div class="' + settings.style.main + '"></div>').appendTo(settings.container);
var win = $(window);
var hash = 'calendar' + (new Date).getTime();
var prevHash;
var showing = false;
input
.attr('readonly', true)
.on('focus click', function(event) {
event.preventDefault();
event.stopImmediatePropagation();
show();
});
if(_.isMoment(settings.min)) {
resetTime(settings.min);
}
if(_.isMoment(settings.max)) {
resetTime(settings.max);
}
widget.html([
'<div>',
'<div class="actions">',
'<a href="#" class="cancel">' + settings.label.cancel + '</a>',
'<a href="#" class="set">' + settings.label.set + '</a>',
settings.label.title,
'</div>',
'<div class="month">',
'<a href="#" class="prev"></a>',
'<div></div>',
'<a href="#" class="next"></a>',
'</div>',
'<div class="weekdays"></div>',
'<div class="days"></div>',
'</div>'
].join(''));
var actionSet = widget.find('div.actions > a.set');
var actionCancel = widget.find('div.actions > a.cancel');
var prevMonth = widget.find('div.month > a.prev');
var nextMonth = widget.find('div.month > a.next');
var labelMonth = widget.find('div.month > div');
var weekDays = widget.find('div.weekdays');
var days = widget.find('div.days');
var date = resetTime(_());
var selected;
var show = function() {
select(val());
if(!_.isMoment(selected)) {
date = resetTime(_());
render();
}
widget.show();
prevHash = location.hash.substring(1);
if(prevHash === hash) {
prevHash = '';
}
showing = true;
location.hash = hash;
emit('show');
};
var hide = function() {
showing = false;
location.hash = prevHash;
widget.hide();
emit('hide');
};
var renderMonth = function() {
labelMonth.text(date.format(settings.format.month));
emit('renderMonth');
};
var renderWeekdays = function() {
var html = '';
var a = date.clone().startOf('week');
var b = date.clone().endOf('week');
while(a <= b) {
html += a.format('[<div>]ddd[</div>]');
a.add('d', 1);
}
weekDays.html(html);
emit('renderWeekDays');
};
var renderDays = function() {
var html = '';
var a = date.clone().startOf('month').startOf('week');
var n = 7 * 6;
while(n > 0) {
html += a >= settings.min && a <= settings.max
? '<a href="#" rel="' + a.valueOf() + '">' + a.date() + '</a>'
: '<span>' + a.date() + '</span>';
a.add('d', 1);
n--;
}
days.html(html);
if(_.isMoment(selected)) {
days.find('[rel="' + selected.valueOf() + '"]').addClass('selected');
}
emit('renderDays');
};
var render = function(direction) {
renderMonth();
renderDays(direction);
emit('render');
};
var emit = function(name) {
var event = $.Event('calendar.' + name);
event.api = api;
input.trigger(event);
};
var val = function () {
return _(input.val(), settings.format.main);
};
var set = function(v) {
v = _(v);
if(_.isMoment(v)) {
resetTime(v);
select(v);
input.val(selected.format(settings.format.main));
emit('set');
input.change();
}
};
var select = function(v) {
v = _(v);
if(_.isMoment(v)) {
resetTime(v);
date = v;
selected = v.clone();
emit('select');
render();
}
};
var addMonth = function() {
date.add('M', 1);
render();
};
var subtractMonth = function() {
date.subtract('M', 1);
render();
};
var x;
var getTouchX = function(event) {
if(event.hasOwnProperty('originalEvent')) {
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0] || null;
if(touch !== null) {
return touch.pageX;
} else {
widget.off('touchend');
}
}
return null;
};
prevMonth.on('click', function(event) {
event.preventDefault();
subtractMonth();
});
nextMonth.on('click', function(event) {
event.preventDefault();
addMonth();
});
actionSet.on('click', function(event) {
event.preventDefault();
set(selected);
hide();
});
actionCancel.on('click', function(event) {
event.preventDefault();
hide();
});
days.on('click', 'a', function(event) {
event.preventDefault();
select(_(parseInt(this.rel)));
});
widget.on('mousewheel', function(event) {
event.preventDefault();
var delta = parseInt(event.wheelDelta || event.detail || event.originalEvent.wheelDelta || event.originalEvent.deltaY);
if(delta > 0) {
addMonth();
} else {
subtractMonth();
}
});
widget.on('touchstart', function(event) {
x = getTouchX(event);
});
widget.on('touchend', function(event) {
var a = getTouchX(event);
var delta = a - x;
x = a;
if(!isNaN(delta) && Math.abs(delta) > settings.tolerance) {
event.preventDefault();
if(delta < -settings.tolerance) {
addMonth();
} else if(delta > settings.tolerance) {
subtractMonth();
}
}
});
win.on('hashchange', function() {
if(!showing && location.hash.substring(1) === hash) {
show();
} else if(showing && location.hash.substring(1) !== hash) {
hide();
}
});
var api = {
settedValue: function(v) {
if(v !== undefined) {
set(v);
return api;
}
return val();
},
selectedValue: function(v) {
if(v !== undefined) {
select(v);
return api;
}
return selected;
},
min: function(v) {
if(v !== undefined) {
settings.min = v;
render();
return api;
}
return settings.min;
},
max: function(v) {
if(v !== undefined) {
settings.max = v;
render();
return api;
}
return settings.max;
},
hash: function() {
return hash;
},
addMonth: addMonth,
subtractMonth: subtractMonth,
show: show,
hide: hide
};
input.data(pluginName, api);
set(input.val());
renderWeekdays();
render();
});
};
})(jQuery, moment);
$(function() {
$('[data-widget-calendar]').CalendarWidget();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment