Created
June 18, 2012 15:13
-
-
Save sirvine/2948855 to your computer and use it in GitHub Desktop.
Kalendae 0.2
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
/******************************************************************** | |
* Kalendae, a framework agnostic javascript date picker * | |
* Copyright(c) 2012 Jarvis Badgley (chipersoft@gmail.com) * | |
* http://github.com/ChiperSoft/Kalendae * | |
* Version 0.2 * | |
********************************************************************/ | |
(function (undefined) { | |
var today; | |
var Kalendae = function (targetElement, options) { | |
//if the first argument isn't an element and isn't a string, assume that it is the options object | |
if (!(targetElement instanceof Element || typeof targetElement === 'string')) options = targetElement; | |
var self = this, | |
classes = self.classes, | |
opts = self.settings = util.merge(self.defaults, {attachTo:targetElement}, options || {}), | |
$container = self.container = util.make('div', {'class':classes.container}), | |
calendars = self.calendars = [], | |
startDay = moment().day(opts.weekStart), | |
vsd, | |
columnHeaders = [], | |
$cal, | |
$title, | |
$caption, | |
$header, | |
$days, dayNodes = [], | |
$span, | |
i = 0, | |
j = opts.months; | |
if (util.isIE8()) util.addClassName($container, 'ie8'); | |
//generate the column headers (Su, Mo, Tu, etc) | |
i = 7; | |
while (i--) { | |
columnHeaders.push( startDay.format('ddd').substr(0,opts.columnHeaderLength) ); | |
startDay.add('days',1); | |
} | |
//setup publish/subscribe and apply any subscriptions passed in settings | |
MinPubSub(self); | |
if (typeof opts.subscribe === 'object') { | |
for (i in opts.subscribe) if (opts.subscribe.hasOwnProperty(i)) { | |
self.subscribe(i, opts.subscribe[i]); | |
} | |
} | |
//process default selected dates | |
self._sel = []; | |
if (!!opts.selected) self.setSelected(opts.selected, false); | |
//set the view month | |
if (!!opts.viewStartDate) { | |
vsd = moment(opts.viewStartDate, opts.format); | |
} else if (self._sel.length > 0) { | |
vsd = moment(self._sel[0]); | |
} else { | |
vsd = moment(); | |
} | |
self.viewStartDate = vsd.date(1); | |
var viewDelta = ({ | |
'past' : opts.months-1, | |
'today-past' : opts.months-1, | |
'any' : opts.months>2?Math.floor(opts.months/2):0, | |
'today-future' : 0, | |
'future' : 0 | |
})[this.settings.direction]; | |
if (viewDelta && moment().month()==moment(self.viewStartDate).month()){ | |
self.viewStartDate = moment(self.viewStartDate).subtract({M:viewDelta}).date(1); | |
} | |
if (typeof opts.blackout === 'function') { | |
self.blackout = opts.blackout; | |
} else if (!!opts.blackout) { | |
var bdates = parseDates(opts.blackout, opts.parseSplitDelimiter); | |
self.blackout = function (input) { | |
input = moment(input).yearDay(); | |
if (input < 1 || !self._sel || self._sel.length < 1) return false; | |
var i = bdates.length; | |
while (i--) if (bdates[i].yearDay() === input) return true; | |
return false; | |
} | |
} else { | |
self.blackout = function () {return false;} | |
} | |
self.direction = self.directions[opts.direction] ? self.directions[opts.direction] : self.directions['any']; | |
//for the total months setting, generate N calendar views and add them to the container | |
j = Math.max(opts.months,1); | |
while (j--) { | |
$cal = util.make('div', {'class':classes.calendar}, $container); | |
$cal.setAttribute('data-cal-index', j); | |
if (opts.months > 1) { | |
if (j == Math.max(opts.months-1,1)) util.addClassName($cal, classes.monthFirst); | |
else if (j === 0) util.addClassName($cal, classes.monthLast); | |
else util.addClassName($cal, classes.monthMiddle); | |
} | |
//title bar | |
$title = util.make('div', {'class':classes.title}, $cal); | |
util.make('a', {'class':classes.previousYear}, $title); //previous button | |
util.make('a', {'class':classes.previousMonth}, $title); //previous button | |
util.make('a', {'class':classes.nextYear}, $title); //next button | |
util.make('a', {'class':classes.nextMonth}, $title); //next button | |
$caption = util.make('span', {'class':classes.caption}, $title); //title caption | |
//column headers | |
$header = util.make('div', {'class':classes.header}, $cal); | |
i = 0; | |
do { | |
$span = util.make('span', {}, $header); | |
$span.innerHTML = columnHeaders[i]; | |
} while (++i < 7) | |
//individual day cells | |
$days = util.make('div', {'class':classes.days}, $cal); | |
i = 0; | |
dayNodes = []; | |
while (i++ < 42) { | |
dayNodes.push(util.make('span', {}, $days)); | |
} | |
//store each calendar view for easy redrawing | |
calendars.push({ | |
caption:$caption, | |
days:dayNodes | |
}); | |
if (j) util.make('div', {'class':classes.monthSeparator}, $container); | |
} | |
self.draw(); | |
util.addEvent($container, 'mousedown', function (event, target) { | |
var clickedDate; | |
if (util.hasClassName(target, classes.nextMonth)) { | |
//NEXT MONTH BUTTON | |
if (!self.disableNext && self.publish('view-changed', self, ['next-month']) !== false) { | |
self.viewStartDate.add('months',1); | |
self.draw(); | |
} | |
return false; | |
} else if (util.hasClassName(target, classes.previousMonth)) { | |
//PREVIOUS MONTH BUTTON | |
if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-month']) !== false) { | |
self.viewStartDate.subtract('months',1); | |
self.draw(); | |
} | |
return false; | |
} else if (util.hasClassName(target, classes.nextYear)) { | |
//NEXT MONTH BUTTON | |
if (!self.disableNext && self.publish('view-changed', self, ['next-year']) !== false) { | |
self.viewStartDate.add('years',1); | |
self.draw(); | |
} | |
return false; | |
} else if (util.hasClassName(target, classes.previousYear)) { | |
//PREVIOUS MONTH BUTTON | |
if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-year']) !== false) { | |
self.viewStartDate.subtract('years',1); | |
self.draw(); | |
} | |
return false; | |
} else if (util.hasClassName(target.parentNode, classes.days) && util.hasClassName(target, classes.dayActive) && (clickedDate = target.getAttribute('data-date'))) { | |
//DAY CLICK | |
clickedDate = moment(clickedDate, opts.dayAttributeFormat).hours(12); | |
if (self.publish('date-clicked', self, [clickedDate]) !== false) { | |
switch (opts.mode) { | |
case 'multiple': | |
if (!self.addSelected(clickedDate)) self.removeSelected(clickedDate); | |
break; | |
case 'range': | |
self.addSelected(clickedDate); | |
break; | |
case 'single': | |
/* falls through */ | |
default: | |
self.addSelected(clickedDate); | |
break; | |
} | |
} | |
return false; | |
} | |
return false; | |
}); | |
if (!!(opts.attachTo = util.$(opts.attachTo))) { | |
opts.attachTo.appendChild($container); | |
} | |
}; | |
Kalendae.prototype = { | |
defaults : { | |
attachTo: null, /* the element to attach the root container to. can be string or DOMElement */ | |
months: 1, /* total number of months to display side by side */ | |
weekStart: 0, /* day to use for the start of the week. 0 is Sunday */ | |
direction: 'any', /* past, today-past, any, today-future, future */ | |
directionScrolling: true, /* if a direction other than any is defined, prevent scrolling out of range */ | |
viewStartDate: null, /* date in the month to display. When multiple months, this is the left most */ | |
blackout: null, /* array of dates, or function to be passed a date */ | |
selected: null, /* dates already selected. can be string, date, or array of strings or dates. */ | |
mode: 'single', /* single, multiple, range */ | |
format: null, /* string used for parsing dates. */ | |
subscribe: null, /* object containing events to subscribe to */ | |
columnHeaderLength: 2, /* number of characters to show in the column headers */ | |
titleFormat: 'MMMM, YYYY', /* format mask for month titles. See momentjs.com for rules */ | |
dayNumberFormat: 'D', /* format mask for individual days */ | |
dayAttributeFormat: 'YYYY-MM-DD', /* format mask for the data-date attribute set on every span */ | |
parseSplitDelimiter: /,\s*|\s+-\s+/, /* regex to use for splitting multiple dates from a passed string */ | |
rangeDelimiter: ' - ', /* string to use between dates when outputting in range mode */ | |
multipleDelimiter: ', ', /* string to use between dates when outputting in multiple mode */ | |
dateClassMap: {} | |
}, | |
classes : { | |
container :'kalendae', | |
calendar :'k-calendar', | |
monthFirst :'k-first-month', | |
monthMiddle :'k-middle-month', | |
monthLast :'k-last-month', | |
title :'k-title', | |
previousMonth :'k-btn-previous-month', | |
nextMonth :'k-btn-next-month', | |
previousYear :'k-btn-previous-year', | |
nextYear :'k-btn-next-year', | |
caption :'k-caption', | |
header :'k-header', | |
days :'k-days', | |
dayOutOfMonth :'k-out-of-month', | |
dayActive :'k-active', | |
daySelected :'k-selected', | |
dayInRange :'k-range', | |
dayToday :'k-today', | |
monthSeparator :'k-separator', | |
disablePreviousMonth :'k-disable-previous-month-btn', | |
disableNextMonth :'k-disable-next-month-btn', | |
disablePreviousYear :'k-disable-previous-year-btn', | |
disableNextYear :'k-disable-next-year-btn' | |
}, | |
disablePreviousMonth: false, | |
disableNextMonth: false, | |
disablePreviousYear: false, | |
disableNextYear: false, | |
directions: { | |
'past' :function (date) {return moment(date).yearDay() >= today.yearDay();}, | |
'today-past' :function (date) {return moment(date).yearDay() > today.yearDay();}, | |
'any' :function (date) {return false;}, | |
'today-future' :function (date) {return moment(date).yearDay() < today.yearDay();}, | |
'future' :function (date) {return moment(date).yearDay() <= today.yearDay();} | |
}, | |
getSelectedAsDates : function () { | |
var out = []; | |
var i=0, c = this._sel.length; | |
for (;i<c;i++) { | |
out.push(this._sel[i].nativeDate()); | |
} | |
return out; | |
}, | |
getSelectedAsText : function (format) { | |
var out = []; | |
var i=0, c = this._sel.length; | |
for (;i<c;i++) { | |
out.push(this._sel[i].format(format || this.settings.format || 'YYYY-MM-DD')) | |
} | |
return out; | |
}, | |
getSelectedRaw : function () { | |
var out = []; | |
var i=0, c = this._sel.length; | |
for (;i<c;i++) { | |
out.push(moment(this._sel[i])) | |
} | |
return out; | |
}, | |
getSelected : function (format) { | |
var sel = this.getSelectedAsText(format); | |
switch (this.settings.mode) { | |
case 'range': | |
sel.splice(2); //shouldn't be more than two, but lets just make sure. | |
return sel.join(this.settings.rangeDelimiter); | |
case 'multiple': | |
return sel.join(this.settings.multipleDelimiter); | |
case 'single': | |
/* falls through */ | |
default: | |
return sel[0]; | |
} | |
}, | |
isSelected : function (input) { | |
input = moment(input).yearDay(); | |
if (input < 1 || !this._sel || this._sel.length < 1) return false; | |
switch (this.settings.mode) { | |
case 'range': | |
var a = this._sel[0] ? this._sel[0].yearDay() : 0, | |
b = this._sel[1] ? this._sel[1].yearDay() : 0; | |
if (a === input || b === input) return 1; | |
if (!a || !b) return 0; | |
if ((input > a && input < b) || (a<b && input < a && input > b)) return -1; | |
return false; | |
case 'multiple': | |
var i = this._sel.length; | |
while (i--) { | |
if (this._sel[i].yearDay() === input) { | |
return true; | |
} | |
} | |
return false; | |
case 'single': | |
/* falls through */ | |
default: | |
return (this._sel[0] && (this._sel[0].yearDay() === input)); | |
} | |
return false; | |
}, | |
setSelected : function (input, draw) { | |
this._sel = parseDates(input, this.settings.parseSplitDelimiter, this.settings.format); | |
this._sel.sort(function (a,b) {return a.yearDay() - b.yearDay();}); | |
if (draw !== false) this.draw(); | |
}, | |
addSelected : function (date, draw) { | |
date = moment(date).hours(12); | |
switch (this.settings.mode) { | |
case 'multiple': | |
if (!this.isSelected(date)) this._sel.push(date); | |
else return false; | |
break; | |
case 'range': | |
if (this._sel.length !== 1) this._sel = [date]; | |
else { | |
if (date.yearDay() > this._sel[0].yearDay()) this._sel[1] = date; | |
else this._sel = [date, this._sel[0]]; | |
} | |
break; | |
case 'single': | |
/* falls through */ | |
default: | |
this._sel = [date]; | |
break; | |
} | |
this._sel.sort(function (a,b) {return a.yearDay() - b.yearDay();}); | |
this.publish('change', this); | |
if (draw !== false) this.draw(); | |
return true; | |
}, | |
removeSelected : function (date, draw) { | |
date = moment(date).yearDay(); | |
var i = this._sel.length; | |
while (i--) { | |
if (this._sel[i].yearDay() === date) { | |
this._sel.splice(i,1); | |
this.publish('change', this); | |
if (draw !== false) this.draw(); | |
return true; | |
} | |
} | |
return false; | |
}, | |
draw : function draw() { | |
// return; | |
var month = moment(this.viewStartDate).hours(12), //force middle of the day to avoid any weird date shifts | |
day, | |
classes = this.classes, | |
cal, | |
$span, | |
klass, | |
i=0, c, | |
j=0, k, | |
s, | |
dateString, | |
opts = this.settings; | |
c = this.calendars.length; | |
do { | |
day = moment(month).date(1); | |
day.day( day.day() < this.settings.weekStart ? this.settings.weekStart-7 : this.settings.weekStart); | |
//if the first day of the month is less than our week start, back up a week | |
cal = this.calendars[i]; | |
cal.caption.innerHTML = month.format(this.settings.titleFormat); | |
j = 0; | |
do { | |
$span = cal.days[j]; | |
klass = []; | |
s = this.isSelected(day); | |
if (s) klass.push(({'-1':classes.dayInRange,'1':classes.daySelected, 'true':classes.daySelected})[s]); | |
if (day.month() != month.month()) klass.push(classes.dayOutOfMonth); | |
else if (!(this.blackout(day) || this.direction(day)) || s>0) klass.push(classes.dayActive); | |
if (day.yearDay() === today.yearDay()) klass.push(classes.dayToday); | |
dateString = day.format(this.settings.dayAttributeFormat); | |
if (opts.dateClassMap[dateString]) klass.push(opts.dateClassMap[dateString]); | |
$span.innerHTML = day.format(opts.dayNumberFormat); | |
$span.className = klass.join(' '); | |
$span.setAttribute('data-date', dateString); | |
day.add('days',1); | |
} while (++j < 42); | |
month.add('months',1); | |
} while (++i < c); | |
if (opts.directionScrolling) { | |
var diff = -(moment().diff(month, 'months')); | |
if (opts.direction==='today-past' || opts.direction==='past') { | |
if (diff <= 0) { | |
this.disableNextMonth = false; | |
util.removeClassName(this.container, classes.disableNextMonth); | |
} else { | |
this.disableNextMonth = true; | |
util.addClassName(this.container, classes.disableNextMonth); | |
} | |
} else if (opts.direction==='today-future' || opts.direction==='future') { | |
if (diff > opts.months) { | |
this.disablePreviousMonth = false; | |
util.removeClassName(this.container, classes.disablePreviousMonth); | |
} else { | |
this.disablePreviousMonth = true; | |
util.addClassName(this.container, classes.disablePreviousMonth); | |
} | |
} | |
if (opts.direction==='today-past' || opts.direction==='past') { | |
if (month.add({Y:1}).diff(moment(), 'years') < 0) { | |
this.disableNextYear = false; | |
util.removeClassName(this.container, classes.disableNextYear); | |
} else { | |
this.disableNextYear = true; | |
util.addClassName(this.container, classes.disableNextYear); | |
} | |
} else if (opts.direction==='today-future' || opts.direction==='future') { | |
if (month.subtract({Y:1}).diff(moment(), 'years') > 0) { | |
this.disablePreviousYear = false; | |
util.removeClassName(this.container, classes.disablePreviousYear); | |
} else { | |
this.disablePreviousYear = true; | |
util.addClassName(this.container, classes.disablePreviousYear); | |
} | |
} | |
} | |
} | |
} | |
var parseDates = function (input, delimiter, format) { | |
var output = []; | |
if (typeof input === 'string') { | |
input = input.split(delimiter); | |
} else if (!util.isArray(input)) { | |
input = [input]; | |
} | |
var c = input.length; | |
i = 0; | |
do { | |
if (input[i]) output.push( moment(input[i], format).hours(12) ); | |
} while (++i < c); | |
return output; | |
} | |
window.Kalendae = Kalendae; | |
var util = Kalendae.util = { | |
isIE8: function() { | |
return !!( (/msie 8./i).test(navigator.appVersion) && !(/opera/i).test(navigator.userAgent) && window.ActiveXObject && XDomainRequest && !window.msPerformance ); | |
}, | |
// ELEMENT FUNCTIONS | |
$: function (elem) { | |
return (typeof elem == 'string') ? document.getElementById(elem) : elem; | |
}, | |
$$: function (selector) { | |
return document.querySelectorAll(selector); | |
}, | |
make: function (tagName, attributes, attach) { | |
var k, e = document.createElement(tagName); | |
if (!!attributes) for (k in attributes) if (attributes.hasOwnProperty(k)) e.setAttribute(k, attributes[k]); | |
if (!!attach) attach.appendChild(e); | |
return e; | |
}, | |
// Returns true if the DOM element is visible, false if it's hidden. | |
// Checks if display is anything other than none. | |
isVisible: function (elem) { | |
// shamelessly copied from jQuery | |
return elem.offsetWidth > 0 || elem.offsetHeight > 0; | |
}, | |
getStyle: function (elem, styleProp) { | |
var y; | |
if (elem.currentStyle) { | |
y = elem.currentStyle[styleProp]; | |
} else if (window.getComputedStyle) { | |
y = window.getComputedStyle(elem, null)[styleProp]; | |
} | |
return y; | |
}, | |
domReady:function (f){/in/.test(document.readyState) ? setTimeout(function() {util.domReady(f);},9) : f()}, | |
// Adds a listener callback to a DOM element which is fired on a specified | |
// event. Callback is sent the event object and the element that triggered the event | |
addEvent: function (elem, eventName, callback) { | |
var listener = function (event) { | |
event = event || window.event; | |
var target = event.target || event.srcElement; | |
var block = callback.apply(elem, [event, target]); | |
if (block === false) { | |
if (!!event.preventDefault) event.preventDefault(); | |
else { | |
event.returnValue = false; | |
event.cancelBubble = true; | |
} | |
} | |
return block; | |
}; | |
if (elem.attachEvent) { // IE only. The "on" is mandatory. | |
elem.attachEvent("on" + eventName, listener); | |
} else { // Other browsers. | |
elem.addEventListener(eventName, listener, false); | |
} | |
return listener; | |
}, | |
// Removes a listener callback from a DOM element which is fired on a specified | |
// event. | |
removeEvent: function (elem, event, listener) { | |
if (elem.detachEvent) { // IE only. The "on" is mandatory. | |
elem.detachEvent("on" + event, listener); | |
} else { // Other browsers. | |
elem.removeEventListener(event, listener, false); | |
} | |
}, | |
hasClassName: function(elem, className) { //copied and modified from Prototype.js | |
if (!(elem = util.$(elem))) return false; | |
var eClassName = elem.className; | |
return (eClassName.length > 0 && (eClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(eClassName))); | |
}, | |
addClassName: function(elem, className) { //copied and modified from Prototype.js | |
if (!(elem = util.$(elem))) return; | |
if (!util.hasClassName(elem, className)) elem.className += (elem.className ? ' ' : '') + className; | |
}, | |
removeClassName: function(elem, className) { //copied and modified from Prototype.js | |
if (!(elem = util.$(elem))) return; | |
elem.className = util.trimString(elem.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ')); | |
}, | |
isFixed: function (elem) { | |
do { | |
if (util.getStyle(elem, 'position') === 'fixed') return true; | |
} while ((elem = elem.offsetParent)); | |
return false; | |
}, | |
getPosition: function (elem, isInner) { | |
var x = elem.offsetLeft, | |
y = elem.offsetTop, | |
r = {}; | |
if (!isInner) { | |
while ((elem = elem.offsetParent)) { | |
x += elem.offsetLeft; | |
y += elem.offsetTop; | |
} | |
} | |
r[0] = r.left = x; | |
r[1] = r.top = y; | |
return r; | |
}, | |
getHeight: function (elem) { | |
return elem.offsetHeight || elem.scrollHeight; | |
}, | |
getWidth: function (elem) { | |
return elem.offsetWidth || elem.scrollWidth; | |
}, | |
// TEXT FUNCTIONS | |
trimString: function (input) { | |
return input.replace(/^\s+/, '').replace(/\s+$/, ''); | |
}, | |
// OBJECT FUNCTIONS | |
merge: function () { | |
/* Combines multiple objects into one. | |
* Syntax: util.extend([true], object1, object2, ... objectN) | |
* If first argument is true, function will merge recursively. | |
*/ | |
var deep = (arguments[0]===true), | |
d = {}, | |
i = deep?1:0; | |
var _c = function (a, b) { | |
if (typeof b !== 'object') return; | |
for (var k in b) if (b.hasOwnProperty(k)) { | |
//if property is an object or array, merge the contents instead of overwriting, if extend() was called as such | |
if (deep && typeof a[k] === 'object' && typeof b[k] === 'object') _update(a[k], b[k]); | |
else a[k] = b[k]; | |
} | |
return a; | |
} | |
for (; i < arguments.length; i++) { | |
_c(d, arguments[i]); | |
} | |
return d; | |
}, | |
isArray: function (array) { | |
return !( | |
!array || | |
(!array.length || array.length === 0) || | |
typeof array !== 'object' || | |
!array.constructor || | |
array.nodeType || | |
array.item | |
); | |
} | |
}; | |
//auto-initializaiton code | |
Kalendae.util.domReady(function () { | |
var els = util.$$('.auto-kal'), | |
i = els.length, | |
e; | |
while (i--) { | |
e = els[i]; | |
if (e.tagName === 'INPUT') { | |
//if element is an input, bind a popup calendar to the input. | |
new Kalendae.Input(e); | |
} else { | |
//otherwise, insert a flat calendar into the element. | |
new Kalendae({attachTo:e}); | |
} | |
} | |
}); | |
Kalendae.Input = function (targetElement, options) { | |
var $input = this.input = util.$(targetElement), | |
overwriteInput; | |
if (!$input || $input.tagName !== 'INPUT') throw "First argument for Kalendae.Input must be an <input> element or a valid element id."; | |
var self = this, | |
classes = self.classes | |
opts = self.settings = util.merge(self.defaults, options); | |
//force attachment to the body | |
opts.attachTo = window.document.body; | |
//if no override provided, use the input's contents | |
if (!opts.selected) opts.selected = $input.value; | |
else overwriteInput = true; | |
//call our parent constructor | |
Kalendae.call(self, opts); | |
//create the close button | |
if (opts.closeButton) { | |
var $closeButton = util.make('a', {'class':classes.closeButton}, self.container) | |
util.addEvent($closeButton, 'click', function () { | |
$input.blur(); | |
}); | |
} | |
if (overwriteInput) $input.value = self.getSelected(); | |
var $container = self.container, | |
noclose = false; | |
$container.style.display = 'none'; | |
util.addClassName($container, classes.positioned); | |
util.addEvent($container, 'mousedown', function (event, target) { | |
noclose = true; //IE8 doesn't obey event blocking when it comes to focusing, so we have to do this shit. | |
}); | |
util.addEvent(window.document, 'mousedown', function (event, target) { | |
noclose = false; | |
}); | |
util.addEvent($input, 'focus', function () { | |
self.setSelected(this.value); | |
self.show(); | |
}); | |
util.addEvent($input, 'blur', function () { | |
if (noclose) { | |
noclose = false; | |
$input.focus(); | |
} | |
else self.hide(); | |
}); | |
util.addEvent($input, 'keyup', function (event) { | |
self.setSelected(this.value); | |
}); | |
self.subscribe('change', function () { | |
$input.value = self.getSelected(); | |
}); | |
}; | |
Kalendae.Input.prototype = util.merge(Kalendae.prototype, { | |
defaults : util.merge(Kalendae.prototype.defaults, { | |
format: 'MM/DD/YYYY', | |
side: 'bottom', | |
closeButton: true, | |
offsetLeft: 0, | |
offsetTop: 0 | |
}), | |
classes : util.merge(Kalendae.prototype.classes, { | |
positioned : 'k-floating', | |
closeButton: 'k-btn-close' | |
}), | |
show : function () { | |
var $container = this.container, | |
style = $container.style, | |
$input = this.input, | |
pos = util.getPosition($input); | |
style.display = ''; | |
switch (opts.side) { | |
case 'left': | |
style.left = (pos.left - util.getWidth($container) + this.settings.offsetLeft) + 'px'; | |
style.top = (pos.top + this.settings.offsetTop) + 'px'; | |
break; | |
case 'right': | |
style.left = (pos.left + util.getWidth($input)) + 'px'; | |
style.top = (pos.top + this.settings.offsetTop) + 'px'; | |
break; | |
case 'top': | |
style.left = (pos.left + this.settings.offsetLeft) + 'px'; | |
style.top = (pos.top - util.getHeight($container) + this.settings.offsetTop) + 'px'; | |
break; | |
case 'bottom': | |
/* falls through */ | |
default: | |
style.left = (pos.left + this.settings.offsetLeft) + 'px'; | |
style.top = (pos.top + util.getHeight($input) + this.settings.offsetTop) + 'px'; | |
break; | |
} | |
style.position = util.isFixed($input) ? 'fixed' : 'absolute'; | |
}, | |
hide : function () { | |
this.container.style.display = 'none'; | |
} | |
}); | |
/*! | |
* MinPubSub, modified for use on Kalendae | |
* Copyright(c) 2011 Daniel Lamb <daniellmb.com> | |
* https://github.com/daniellmb/MinPubSub | |
* MIT Licensed | |
*/ | |
var MinPubSub = function(d){ | |
if (!d) d = this; | |
// the topic/subscription hash | |
var cache = d.c_ || {}; //check for "c_" cache for unit testing | |
d.publish = function(/* String */ topic, /* Object */ target, /* Array? */ args){ | |
// summary: | |
// Publish some data on a named topic. | |
// topic: String | |
// The channel to publish on | |
// args: Array? | |
// The data to publish. Each array item is converted into an ordered | |
// arguments on the subscribed functions. | |
// | |
// example: | |
// Publish stuff on '/some/topic'. Anything subscribed will be called | |
// with a function signature like: function(a,b,c){ ... } | |
// | |
// publish("/some/topic", ["a","b","c"]); | |
var subs = cache[topic], | |
len = subs ? subs.length : 0, | |
r; | |
//can change loop or reverse array if the order matters | |
while(len--){ | |
r = subs[len].apply(target, args || []); | |
if (typeof r === 'boolean') return r; | |
} | |
}; | |
d.subscribe = function(/* String */ topic, /* Function */ callback, /* Boolean */ topPriority){ | |
// summary: | |
// Register a callback on a named topic. | |
// topic: String | |
// The channel to subscribe to | |
// callback: Function | |
// The handler event. Anytime something is publish'ed on a | |
// subscribed channel, the callback will be called with the | |
// published array as ordered arguments. | |
// | |
// returns: Array | |
// A handle which can be used to unsubscribe this particular subscription. | |
// | |
// example: | |
// subscribe("/some/topic", function(a, b, c){ /* handle data */ }); | |
if(!cache[topic]){ | |
cache[topic] = []; | |
} | |
if (topPriority) | |
cache[topic].push(callback); | |
else | |
cache[topic].unshift(callback); | |
return [topic, callback]; // Array | |
}; | |
d.unsubscribe = function(/* Array */ handle){ | |
// summary: | |
// Disconnect a subscribed function for a topic. | |
// handle: Array | |
// The return value from a subscribe call. | |
// example: | |
// var handle = subscribe("/some/topic", function(){}); | |
// unsubscribe(handle); | |
var subs = cache[handle[0]], | |
callback = handle[1], | |
len = subs ? subs.length : 0; | |
while(len--){ | |
if(subs[len] === callback){ | |
subs.splice(len, 1); | |
} | |
} | |
}; | |
};// Moment.js | |
// Altered slightly for use in Kalendae.js | |
// | |
// (c) 2011 Tim Wood | |
// Moment.js is freely distributable under the terms of the MIT license. | |
// | |
// Version 1.3.0 | |
var moment = Kalendae.moment = (function (Date, undefined) { | |
var moment, | |
round = Math.round, | |
languages = {}, | |
hasModule = (typeof module !== 'undefined'), | |
paramsToParse = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'), | |
i, | |
charactersToReplace = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g, | |
nonuppercaseLetters = /[^A-Z]/g, | |
timezoneRegex = /\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g, | |
tokenCharacters = /(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g, | |
inputCharacters = /(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi, | |
timezoneParseRegex = /([\+\-]|\d\d)/gi, | |
VERSION = "1.3.0", | |
shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'); | |
// Moment prototype object | |
function Moment(date) { | |
this._d = date; | |
} | |
// left zero fill a number | |
// see http://jsperf.com/left-zero-filling for performance comparison | |
function leftZeroFill(number, targetLength) { | |
var output = number + ''; | |
while (output.length < targetLength) { | |
output = '0' + output; | |
} | |
return output; | |
} | |
// helper function for _.addTime and _.subtractTime | |
function dateAddRemove(date, _input, adding, val) { | |
var isString = (typeof _input === 'string'), | |
input = isString ? {} : _input, | |
ms, d, M, currentDate; | |
if (isString && val) { | |
input[_input] = val; | |
} | |
ms = (input.ms || input.milliseconds || 0) + | |
(input.s || input.seconds || 0) * 1e3 + // 1000 | |
(input.m || input.minutes || 0) * 6e4 + // 1000 * 60 | |
(input.h || input.hours || 0) * 36e5; // 1000 * 60 * 60 | |
d = (input.d || input.days || 0) + | |
(input.w || input.weeks || 0) * 7; | |
M = (input.M || input.months || 0) + | |
(input.y || input.years || 0) * 12; | |
if (ms) { | |
date.setTime(+date + ms * adding); | |
} | |
if (d) { | |
date.setDate(date.getDate() + d * adding); | |
} | |
if (M) { | |
currentDate = date.getDate(); | |
date.setDate(1); | |
date.setMonth(date.getMonth() + M * adding); | |
date.setDate(Math.min(new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(), currentDate)); | |
} | |
return date; | |
} | |
// check if is an array | |
function isArray(input) { | |
return Object.prototype.toString.call(input) === '[object Array]'; | |
} | |
// convert an array to a date. | |
// the array should mirror the parameters below | |
// note: all values past the year are optional and will default to the lowest possible value. | |
// [year, month, day , hour, minute, second, millisecond] | |
function dateFromArray(input) { | |
return new Date(input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0); | |
} | |
// format date using native date object | |
function formatDate(date, inputString) { | |
var m = new Moment(date), | |
currentMonth = m.month(), | |
currentDate = m.date(), | |
currentYear = m.year(), | |
currentDay = m.day(), | |
currentHours = m.hours(), | |
currentMinutes = m.minutes(), | |
currentSeconds = m.seconds(), | |
currentZone = m.zone(), | |
ordinal = moment.ordinal, | |
meridiem = moment.meridiem; | |
// check if the character is a format | |
// return formatted string or non string. | |
// | |
// uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380) | |
// for minification and performance | |
// see http://jsperf.com/object-of-functions-vs-switch for performance comparison | |
function replaceFunction(input) { | |
// create a couple variables to be used later inside one of the cases. | |
var a, b; | |
switch (input) { | |
// MONTH | |
case 'M' : | |
return currentMonth + 1; | |
case 'Mo' : | |
return (currentMonth + 1) + ordinal(currentMonth + 1); | |
case 'MM' : | |
return leftZeroFill(currentMonth + 1, 2); | |
case 'MMM' : | |
return moment.monthsShort[currentMonth]; | |
case 'MMMM' : | |
return moment.months[currentMonth]; | |
// DAY OF MONTH | |
case 'D' : | |
return currentDate; | |
case 'Do' : | |
return currentDate + ordinal(currentDate); | |
case 'DD' : | |
return leftZeroFill(currentDate, 2); | |
// DAY OF YEAR | |
case 'DDD' : | |
a = new Date(currentYear, currentMonth, currentDate); | |
b = new Date(currentYear, 0, 1); | |
return ~~ (((a - b) / 864e5) + 1.5); | |
case 'DDDo' : | |
a = replaceFunction('DDD'); | |
return a + ordinal(a); | |
case 'DDDD' : | |
return leftZeroFill(replaceFunction('DDD'), 3); | |
// WEEKDAY | |
case 'd' : | |
return currentDay; | |
case 'do' : | |
return currentDay + ordinal(currentDay); | |
case 'ddd' : | |
return moment.weekdaysShort[currentDay]; | |
case 'dddd' : | |
return moment.weekdays[currentDay]; | |
// WEEK OF YEAR | |
case 'w' : | |
a = new Date(currentYear, currentMonth, currentDate - currentDay + 5); | |
b = new Date(a.getFullYear(), 0, 4); | |
return ~~ ((a - b) / 864e5 / 7 + 1.5); | |
case 'wo' : | |
a = replaceFunction('w'); | |
return a + ordinal(a); | |
case 'ww' : | |
return leftZeroFill(replaceFunction('w'), 2); | |
// YEAR | |
case 'YY' : | |
return leftZeroFill(currentYear % 100, 2); | |
case 'YYYY' : | |
return currentYear; | |
// AM / PM | |
case 'a' : | |
return currentHours > 11 ? meridiem.pm : meridiem.am; | |
case 'A' : | |
return currentHours > 11 ? meridiem.PM : meridiem.AM; | |
// 24 HOUR | |
case 'H' : | |
return currentHours; | |
case 'HH' : | |
return leftZeroFill(currentHours, 2); | |
// 12 HOUR | |
case 'h' : | |
return currentHours % 12 || 12; | |
case 'hh' : | |
return leftZeroFill(currentHours % 12 || 12, 2); | |
// MINUTE | |
case 'm' : | |
return currentMinutes; | |
case 'mm' : | |
return leftZeroFill(currentMinutes, 2); | |
// SECOND | |
case 's' : | |
return currentSeconds; | |
case 'ss' : | |
return leftZeroFill(currentSeconds, 2); | |
// TIMEZONE | |
case 'zz' : | |
// depreciating 'zz' fall through to 'z' | |
case 'z' : | |
return (date.toString().match(timezoneRegex) || [''])[0].replace(nonuppercaseLetters, ''); | |
case 'Z' : | |
return (currentZone > 0 ? '+' : '-') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2); | |
case 'ZZ' : | |
return (currentZone > 0 ? '+' : '-') + leftZeroFill(~~(10 * Math.abs(currentZone) / 6), 4); | |
// LONG DATES | |
case 'L' : | |
case 'LL' : | |
case 'LLL' : | |
case 'LLLL' : | |
case 'LT' : | |
return formatDate(date, moment.longDateFormat[input]); | |
// DEFAULT | |
default : | |
return input.replace(/(^\[)|(\\)|\]$/g, ""); | |
} | |
} | |
return inputString.replace(charactersToReplace, replaceFunction); | |
} | |
// date from string and format string | |
function makeDateFromStringAndFormat(string, format) { | |
var inArray = [0, 0, 1, 0, 0, 0, 0], | |
timezoneHours = 0, | |
timezoneMinutes = 0, | |
isUsingUTC = false, | |
inputParts = string.match(inputCharacters), | |
formatParts = format.match(tokenCharacters), | |
i, | |
isPm; | |
// function to convert string input to date | |
function addTime(format, input) { | |
var a; | |
switch (format) { | |
// MONTH | |
case 'M' : | |
// fall through to MM | |
case 'MM' : | |
inArray[1] = ~~input - 1; | |
break; | |
case 'MMM' : | |
// fall through to MMMM | |
case 'MMMM' : | |
for (a = 0; a < 12; a++) { | |
if (moment.monthsParse[a].test(input)) { | |
inArray[1] = a; | |
break; | |
} | |
} | |
break; | |
// DAY OF MONTH | |
case 'D' : | |
// fall through to DDDD | |
case 'DD' : | |
// fall through to DDDD | |
case 'DDD' : | |
// fall through to DDDD | |
case 'DDDD' : | |
inArray[2] = ~~input; | |
break; | |
// YEAR | |
case 'YY' : | |
input = ~~input; | |
inArray[0] = input + (input > 70 ? 1900 : 2000); | |
break; | |
case 'YYYY' : | |
inArray[0] = ~~Math.abs(input); | |
break; | |
// AM / PM | |
case 'a' : | |
// fall through to A | |
case 'A' : | |
isPm = (input.toLowerCase() === 'pm'); | |
break; | |
// 24 HOUR | |
case 'H' : | |
// fall through to hh | |
case 'HH' : | |
// fall through to hh | |
case 'h' : | |
// fall through to hh | |
case 'hh' : | |
inArray[3] = ~~input; | |
break; | |
// MINUTE | |
case 'm' : | |
// fall through to mm | |
case 'mm' : | |
inArray[4] = ~~input; | |
break; | |
// SECOND | |
case 's' : | |
// fall through to ss | |
case 'ss' : | |
inArray[5] = ~~input; | |
break; | |
// TIMEZONE | |
case 'Z' : | |
// fall through to ZZ | |
case 'ZZ' : | |
isUsingUTC = true; | |
a = input.match(timezoneParseRegex); | |
if (a[1]) { | |
timezoneHours = ~~a[1]; | |
} | |
if (a[2]) { | |
timezoneMinutes = ~~a[2]; | |
} | |
// reverse offsets | |
if (a[0] === '-') { | |
timezoneHours = -timezoneHours; | |
timezoneMinutes = -timezoneMinutes; | |
} | |
break; | |
} | |
} | |
for (i = 0; i < formatParts.length; i++) { | |
addTime(formatParts[i], inputParts[i]); | |
} | |
// handle am pm | |
if (isPm && inArray[3] < 12) { | |
inArray[3] += 12; | |
} | |
// if is 12 am, change hours to 0 | |
if (isPm === false && inArray[3] === 12) { | |
inArray[3] = 0; | |
} | |
// handle timezone | |
inArray[3] += timezoneHours; | |
inArray[4] += timezoneMinutes; | |
// return | |
return isUsingUTC ? new Date(Date.UTC.apply({}, inArray)) : dateFromArray(inArray); | |
} | |
// compare two arrays, return the number of differences | |
function compareArrays(array1, array2) { | |
var len = Math.min(array1.length, array2.length), | |
lengthDiff = Math.abs(array1.length - array2.length), | |
diffs = 0, | |
i; | |
for (i = 0; i < len; i++) { | |
if (~~array1[i] !== ~~array2[i]) { | |
diffs++; | |
} | |
} | |
return diffs + lengthDiff; | |
} | |
// date from string and array of format strings | |
function makeDateFromStringAndArray(string, formats) { | |
var output, | |
inputParts = string.match(inputCharacters), | |
scores = [], | |
scoreToBeat = 99, | |
i, | |
curDate, | |
curScore; | |
for (i = 0; i < formats.length; i++) { | |
curDate = makeDateFromStringAndFormat(string, formats[i]); | |
curScore = compareArrays(inputParts, formatDate(curDate, formats[i]).match(inputCharacters)); | |
if (curScore < scoreToBeat) { | |
scoreToBeat = curScore; | |
output = curDate; | |
} | |
} | |
return output; | |
} | |
moment = function (input, format) { | |
if (input === null) { | |
return null; | |
} | |
var date; | |
// parse UnderscoreDate object | |
if (input && input._d instanceof Date) { | |
date = new Date(+input._d); | |
// parse string and format | |
} else if (format) { | |
if (isArray(format)) { | |
date = makeDateFromStringAndArray(input, format); | |
} else { | |
date = makeDateFromStringAndFormat(input, format); | |
} | |
// parse everything else | |
} else { | |
date = input === undefined ? new Date() : | |
input instanceof Date ? input : | |
isArray(input) ? dateFromArray(input) : | |
new Date(input); | |
} | |
return new Moment(date); | |
}; | |
// version number | |
moment.version = VERSION; | |
// language switching and caching | |
moment.lang = function (key, values) { | |
var i, | |
param, | |
req, | |
parse = []; | |
if (values) { | |
for (i = 0; i < 12; i++) { | |
parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i'); | |
} | |
values.monthsParse = values.monthsParse || parse; | |
languages[key] = values; | |
} | |
if (languages[key]) { | |
for (i = 0; i < paramsToParse.length; i++) { | |
param = paramsToParse[i]; | |
moment[param] = languages[key][param] || moment[param]; | |
} | |
} else { | |
if (hasModule) { | |
req = require('./lang/' + key); | |
moment.lang(key, req); | |
} | |
} | |
}; | |
// set default language | |
moment.lang('en', { | |
months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), | |
monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), | |
weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), | |
weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), | |
longDateFormat : { | |
LT : "h:mm A", | |
L : "MM/DD/YYYY", | |
LL : "MMMM D YYYY", | |
LLL : "MMMM D YYYY LT", | |
LLLL : "dddd, MMMM D YYYY LT" | |
}, | |
meridiem : { | |
AM : 'AM', | |
am : 'am', | |
PM : 'PM', | |
pm : 'pm' | |
}, | |
calendar : { | |
sameDay : '[Today at] LT', | |
nextDay : '[Tomorrow at] LT', | |
nextWeek : 'dddd [at] LT', | |
lastDay : '[Yesterday at] LT', | |
lastWeek : '[last] dddd [at] LT', | |
sameElse : 'L' | |
}, | |
relativeTime : { | |
future : "in %s", | |
past : "%s ago", | |
s : "a few seconds", | |
m : "a minute", | |
mm : "%d minutes", | |
h : "an hour", | |
hh : "%d hours", | |
d : "a day", | |
dd : "%d days", | |
M : "a month", | |
MM : "%d months", | |
y : "a year", | |
yy : "%d years" | |
}, | |
ordinal : function (number) { | |
var b = number % 10; | |
return (~~ (number % 100 / 10) === 1) ? 'th' : | |
(b === 1) ? 'st' : | |
(b === 2) ? 'nd' : | |
(b === 3) ? 'rd' : 'th'; | |
} | |
}); | |
// helper function for _date.from() and _date.fromNow() | |
function substituteTimeAgo(string, number, withoutSuffix) { | |
var rt = moment.relativeTime[string]; | |
return (typeof rt === 'function') ? | |
rt(number || 1, !!withoutSuffix, string) : | |
rt.replace(/%d/i, number || 1); | |
} | |
function relativeTime(milliseconds, withoutSuffix) { | |
var seconds = round(Math.abs(milliseconds) / 1000), | |
minutes = round(seconds / 60), | |
hours = round(minutes / 60), | |
days = round(hours / 24), | |
years = round(days / 365), | |
args = seconds < 45 && ['s', seconds] || | |
minutes === 1 && ['m'] || | |
minutes < 45 && ['mm', minutes] || | |
hours === 1 && ['h'] || | |
hours < 22 && ['hh', hours] || | |
days === 1 && ['d'] || | |
days <= 25 && ['dd', days] || | |
days <= 45 && ['M'] || | |
days < 345 && ['MM', round(days / 30)] || | |
years === 1 && ['y'] || ['yy', years]; | |
args[2] = withoutSuffix; | |
return substituteTimeAgo.apply({}, args); | |
} | |
// shortcut for prototype | |
moment.fn = Moment.prototype = { | |
clone : function () { | |
return moment(this); | |
}, | |
valueOf : function () { | |
return +this._d; | |
}, | |
nativeDate : function () { | |
return this._d; | |
}, | |
toString : function () { | |
return this._d.toString(); | |
}, | |
toDate : function () { | |
return this._d; | |
}, | |
format : function (inputString) { | |
return formatDate(this._d, inputString); | |
}, | |
add : function (input, val) { | |
this._d = dateAddRemove(this._d, input, 1, val); | |
return this; | |
}, | |
subtract : function (input, val) { | |
this._d = dateAddRemove(this._d, input, -1, val); | |
return this; | |
}, | |
diff : function (input, val, asFloat) { | |
var inputMoment = moment(input), | |
diff = this._d - inputMoment._d, | |
year = this.year() - inputMoment.year(), | |
month = this.month() - inputMoment.month(), | |
day = this.day() - inputMoment.day(), | |
output; | |
if (val === 'months') { | |
output = year * 12 + month + day / 30; | |
} else if (val === 'years') { | |
output = year + month / 12; | |
} else { | |
output = val === 'seconds' ? diff / 1e3 : // 1000 | |
val === 'minutes' ? diff / 6e4 : // 1000 * 60 | |
val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60 | |
val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24 | |
val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7 | |
val === 'days' ? diff / 3600 : diff; | |
} | |
return asFloat ? output : round(output); | |
}, | |
from : function (time, withoutSuffix) { | |
var difference = this.diff(time), | |
rel = moment.relativeTime, | |
output = relativeTime(difference, withoutSuffix); | |
return withoutSuffix ? output : (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output); | |
}, | |
fromNow : function (withoutSuffix) { | |
return this.from(moment(), withoutSuffix); | |
}, | |
calendar : function () { | |
var today = moment(), | |
todayAtZeroHour = moment([today.year(), today.month(), today.date()]), | |
diff = this.diff(todayAtZeroHour, 'days', true), | |
calendar = moment.calendar, | |
allElse = calendar.sameElse, | |
format = diff < -6 ? allElse : | |
diff < -1 ? calendar.lastWeek : | |
diff < 0 ? calendar.lastDay : | |
diff < 1 ? calendar.sameDay : | |
diff < 2 ? calendar.nextDay : | |
diff < 7 ? calendar.nextWeek : allElse; | |
return this.format(typeof format === 'function' ? format.apply(this) : format); | |
}, | |
isLeapYear : function () { | |
var year = this.year(); | |
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; | |
}, | |
isDST : function () { | |
return this.zone() !== moment([this.year()]).zone(); | |
}, | |
day : function (input) { | |
var day = this._d.getDay(); | |
return (typeof input === 'undefined') ? day : | |
this.add({ d : input - day }); | |
} | |
}; | |
// helper for adding shortcuts | |
function makeShortcut(name, key) { | |
moment.fn[name] = function (input) { | |
if (typeof input !== 'undefined') { | |
this._d['set' + key](input); | |
return this; | |
} else { | |
return this._d['get' + key](); | |
} | |
}; | |
} | |
// loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) | |
for (i = 0; i < shortcuts.length; i ++) { | |
makeShortcut(shortcuts[i].toLowerCase(), shortcuts[i]); | |
} | |
// add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') | |
makeShortcut('year', 'FullYear'); | |
// add shortcut for timezone offset (no setter) | |
moment.fn.zone = function () { | |
return this._d.getTimezoneOffset(); | |
}; | |
return moment; | |
})(Date); | |
//function to reset the date object to 00:00 GMT | |
moment.fn.stripTime = function () { | |
this._d = new Date(Math.floor(this._d.valueOf() / 86400000) * 86400000); | |
return this; | |
} | |
//function to get the total number of days since the epoch. | |
moment.fn.yearDay = function (input) { | |
var yearday = Math.floor(this._d / 86400000); | |
return (typeof input === 'undefined') ? yearday : | |
this.add({ d : input - yearday }); | |
} | |
today = moment().stripTime(); | |
if (typeof jQuery !== 'undefined') { | |
jQuery.fn.kalendae = function (options) { | |
this.each(function (i, e) { | |
if (e.tagName === 'INPUT') { | |
//if element is an input, bind a popup calendar to the input. | |
$(e).data('kalendae', new Kalendae.Input(e, options)); | |
} else { | |
//otherwise, insert a flat calendar into the element. | |
$(e).data('kalendae', new Kalendae($.extend({}, {attachTo:e}, options))); | |
} | |
}); | |
return this; | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment