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); } }