vendor/assets/javascripts/kalendae.js in kalendae_assets-0.1.0 vs vendor/assets/javascripts/kalendae.js in kalendae_assets-0.1.1
- old
+ new
@@ -10,11 +10,11 @@
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 = [],
@@ -27,26 +27,26 @@
$header,
$days, dayNodes = [],
$span,
i = 0,
j = opts.months;
-
+
//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
@@ -56,49 +56,49 @@
vsd = moment(self._sel[0]);
} else {
vsd = moment();
}
self.viewStartDate = vsd.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).hours(0).minutes(0).seconds(0).milliseconds(0).valueOf();
if (input < 1 || !self._sel || self._sel.length < 1) return false;
var i = bdates.length;
while (i--) if (bdates[i].valueOf() === input) return true;
- return false;
+ 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.previous}, $title); //previous button
util.make('a', {'class':classes.next}, $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);
@@ -116,40 +116,40 @@
//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.next)) {
//NEXT MONTH BUTTON
if (self.publish('view-changed', self, ['next']) !== false) {
self.viewStartDate.add('months',1);
self.draw();
}
- return false;
-
+ return false;
+
} else if (util.hasClassName(target, classes.previous)) {
//PREVIOUS MONTH BUTTON
if (self.publish('view-changed', self, ['previous']) !== false) {
self.viewStartDate.subtract('months',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);
if (self.publish('date-clicked', self, [clickedDate]) !== false) {
-
+
switch (opts.mode) {
case 'multiple':
if (!self.addSelected(clickedDate)) self.removeSelected(clickedDate);
break;
case 'range':
@@ -162,20 +162,20 @@
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 */
@@ -191,14 +191,14 @@
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 */
+ 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',
@@ -216,46 +216,46 @@
daySelected :'k-selected',
dayInRange :'k-range',
dayToday :'k-today',
monthSeparator :'k-separator'
},
-
+
directions: {
- 'past' :function (date) {return moment(date).valueOf() >= today.valueOf();},
- 'today-past' :function (date) {return moment(date).valueOf() > today.valueOf();},
- 'any' :function (date) {return false;},
- 'today-future' :function (date) {return moment(date).valueOf() < today.valueOf();},
+ 'past' :function (date) {return moment(date).valueOf() >= today.valueOf();},
+ 'today-past' :function (date) {return moment(date).valueOf() > today.valueOf();},
+ 'any' :function (date) {return false;},
+ 'today-future' :function (date) {return moment(date).valueOf() < today.valueOf();},
'future' :function (date) {return moment(date).valueOf() <= today.valueOf();}
},
-
+
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.
@@ -268,11 +268,11 @@
/* falls through */
default:
return sel[0];
}
},
-
+
isSelected : function (input) {
input = moment(input).hours(0).minutes(0).seconds(0).milliseconds(0).valueOf();
if (input < 1 || !this._sel || this._sel.length < 1) return false;
switch (this.settings.mode) {
@@ -302,18 +302,18 @@
return (this._sel[0] && (this._sel[0].valueOf() === 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.valueOf() - b.valueOf();});
if (draw !== false) this.draw();
},
-
+
addSelected : function (date, draw) {
date = moment(date).hours(0).minutes(0).seconds(0).milliseconds(0);
switch (this.settings.mode) {
case 'multiple':
if (!this.isSelected(date)) this._sel.push(date);
@@ -336,11 +336,11 @@
this._sel.sort(function (a,b) {return a.valueOf() - b.valueOf();});
this.publish('change', this);
if (draw !== false) this.draw();
return true;
},
-
+
removeSelected : function (date, draw) {
date = moment(date).hours(0).minutes(0).seconds(0).milliseconds(0).valueOf();
var i = this._sel.length;
while (i--) {
if (this._sel[i].valueOf() === date) {
@@ -350,11 +350,11 @@
return true;
}
}
return false;
},
-
+
draw : function draw() {
// return;
var month = moment(this.viewStartDate),
day,
classes = this.classes,
@@ -366,24 +366,24 @@
s,
dateString,
opts = this.settings;
c = this.calendars.length;
-
+
var viewDelta = ({
'past' : c-1,
'today-past' : c-1,
'any' : c>2?Math.floor(c/2):0,
'today-future' : 0,
'future' : 0
})[this.settings.direction];
-
+
if (viewDelta) month = month.subtract({M:viewDelta});
do {
day = moment(month).date(1);
- day.day( day.day() < this.settings.weekStart ? this.settings.weekStart-7 : this.settings.weekStart);
+ 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;
@@ -405,35 +405,35 @@
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);
}
}
var parseDates = function (input, delimiter, format) {
var output = [];
-
+
if (typeof input === 'string') {
- input = input.split(delimiter);
+ input = input.split(delimiter);
} else if (!util.isArray(input)) {
input = [input];
}
-
+
c = input.length;
i = 0;
do {
if (input[i]) output.push( moment(input[i], format).hours(0).minutes(0).seconds(0).milliseconds(0) );
} while (++i < c);
-
+
return output;
}
@@ -443,15 +443,15 @@
// 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;
@@ -461,11 +461,11 @@
// Checks if display is anything other than none.
isVisible: function (elem) {
// shamelessly copied from jQuery
return elem.offsetWidth > 0 || elem.offsetHeight > 0;
},
-
+
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) {
@@ -497,11 +497,11 @@
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)));
},
@@ -518,18 +518,18 @@
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;
},
@@ -538,27 +538,27 @@
},
getWidth: function (elem) {
return elem.offsetWidth || elem.scrollWidth;
},
-
-
-// TEXT FUNCTIONS
-
+
+
+// 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) {
@@ -574,19 +574,19 @@
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
+ !array ||
+ (!array.length || array.length === 0) ||
+ typeof array !== 'object' ||
+ !array.constructor ||
+ array.nodeType ||
+ array.item
);
}
};
@@ -603,42 +603,42 @@
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);
-
+
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;
@@ -646,26 +646,26 @@
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',
@@ -673,19 +673,19 @@
offsetLeft: 0,
offsetTop: 0
}),
classes : util.merge(Kalendae.prototype.classes, {
positioned : 'k-floating'
-
+
}),
-
+
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';
@@ -703,17 +703,17 @@
default:
style.left = (pos.left + this.settings.offsetLeft) + 'px';
style.top = (pos.top + util.getHeight($input) + this.settings.offsetTop) + 'px';
break;
}
-
+
},
-
+
hide : function () {
this.container.style.display = 'none';
}
-
+
});
/*!
* MinPubSub, modified for use on Kalendae
@@ -726,26 +726,26 @@
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:
+ // 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.
+ // 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
@@ -759,17 +759,17 @@
// 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
+ // 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] = [];
@@ -787,14 +787,14 @@
// 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);
}
}