diff --git a/coffee/.gitignore b/coffee/.gitignore deleted file mode 100644 index a30fff1..0000000 --- a/coffee/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Directories -.cache -.sass-cache -node_modules - -# Files -npm-debug.log -*~ \ No newline at end of file diff --git a/coffee/.sass-lint.yml b/coffee/.sass-lint.yml deleted file mode 100644 index c0f7caf..0000000 --- a/coffee/.sass-lint.yml +++ /dev/null @@ -1,146 +0,0 @@ -# Rule Configuration -rules: - attribute-quotes: - - 2 - border-zero: - - convention: 'none' - brace-style: - - - style: '1tbs' - allow-single-line: false - class-name-format: - - 2 - - - allow-leading-underscore: true - convention: hyphenatedbem - empty-args: - - 2 - - include: true - empty-line-between-blocks: - - 2 - - - include: true - allow-single-line-rulesets: false - extends-before-declarations: - - 2 - extends-before-mixins: - - 2 - final-newline: - - 2 - - include: false - force-attribute-nesting: - - 0 - force-element-nesting: - - 0 - force-pseudo-nesting: - - 0 - function-name-format: - - 2 - - - allow-leading-underscore: true - convention: hyphenatedbem - hex-length: - - 2 - - style: short - hex-notation: - - 2 - - style: lowercase - id-name-format: - - 2 - - - allow-leading-underscore: true - convention: hyphenatedlowercase - indentation: - - 1 - - size: 4 - leading-zero: - - 2 - - include: true - mixin-name-format: - - 0 - mixins-before-declarations: - - 0 - nesting-depth: - - 2 - - max-depth: 4 - no-attribute-selectors: - - 2 - no-color-hex: - - 0 - no-color-keywords: - - 2 - no-color-literals: - - 0 - no-combinator: - - 0 - no-css-comments: - - 0 - no-duplicate-properties: - - 2 - no-empty-rulesets: - - 2 - no-important: - - 0 - no-invalid-hex: - - 2 - no-mergeable-selectors: - - 2 - no-misspelled-properties: - - 2 - no-trailing-whitespace: - - 2 - no-transition-all: - - 2 - no-universal-selectors: - - 0 - no-vendor-prefixes: - - 0 - one-declaration-per-line: - - 2 - property-sort-order: - - 2 - - order: 'alphabetical' - quotes: - - 2 - - style: 'single' - shorthand-values: - - 2 - - allowed-shorthands: [1, 2, 3] - single-line-per-selector: - - 2 - space-after-bang: - - 2 - - include: 'false' - space-after-colon: - - 2 - - include: true - space-after-comma: - - 2 - - include: true - space-around-operator: - - 2 - - include: true - space-before-bang: - - 2 - - include: true - space-before-brace: - - 2 - - include: true - space-before-colon: - - 2 - - include: false - space-between-parens: - - 2 - - include: false - trailing-semicolon: - - 2 - - include: true - url-quotes: - - 2 - variable-name-format: - - 2 - - - allow-leading-underscore: true - convention: hyphenatedbem - zero-unit: - - 0 \ No newline at end of file diff --git a/coffee/README.md b/coffee/README.md deleted file mode 100644 index 8c91b54..0000000 --- a/coffee/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# manhattan-date-picker - -Date picker support for form fields. \ No newline at end of file diff --git a/coffee/coffeelint.json b/coffee/coffeelint.json deleted file mode 100644 index 7a409c1..0000000 --- a/coffee/coffeelint.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "arrow_spacing": { - "level": "error" - }, - "braces_spacing": { - "level": "error", - "empty_object_spaces": 0, - "spaces": 0 - }, - "camel_case_classes": { - "level": "error" - }, - "colon_assignment_spacing": { - "level": "error", - "spacing": { - "left": 0, - "right": 1 - } - }, - "empty_constructor_needs_parens": { - "level": "error" - }, - "ensure_comprehensions": { - "level": "error" - }, - "indentation": { - "level": "error", - "value": 4 - }, - "newlines_after_classes": { - "level": "ignore" - }, - "no_implicit_braces": { - "level": "ignore" - }, - "no_stand_alone_at": { - "level": "error" - }, - "no_this": { - "level": "error" - }, - "no_unnecessary_double_quotes": { - "level": "error" - }, - "non_empty_constructor_needs_parens": { - "level": "error" - }, - "space_operators": { - "level": "ignore" - }, - "spacing_after_comma": { - "level": "error" - } -} \ No newline at end of file diff --git a/coffee/dist/date-picker.css b/coffee/dist/date-picker.css deleted file mode 100644 index bc406d0..0000000 --- a/coffee/dist/date-picker.css +++ /dev/null @@ -1,136 +0,0 @@ -.mh-calendar { - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - box-sizing: border-box; - float: left; - font-family: sans-serif; - padding: 10px; - user-select: none; - width: 300px; } - .mh-calendar * { - box-sizing: border-box; } - .mh-calendar__nav { - position: relative; } - .mh-calendar__month { - color: #666; - font-size: 18px; - text-align: center; } - .mh-calendar__next { - border-bottom: 8px solid transparent; - border-left: 12px solid #334144; - border-radius: 3px; - border-top: 8px solid transparent; - cursor: pointer; - height: 0; - position: absolute; - right: 10px; - top: 2px; - width: 0; } - .mh-calendar__previous { - border-bottom: 8px solid transparent; - border-radius: 3px; - border-right: 12px solid #334144; - border-top: 8px solid transparent; - cursor: pointer; - height: 0; - left: 10px; - position: absolute; - top: 2px; - width: 0; } - .mh-calendar__weekdays { - margin-top: 10px; } - .mh-calendar__weekdays::after { - clear: both; - content: ' '; - display: table; } - .mh-calendar__weekday { - color: #999; - float: left; - font-size: 11px; - font-weight: bold; - text-align: center; - width: 40px; } - .mh-calendar__dates { - margin-top: 10px; } - .mh-calendar__dates::after { - clear: both; - content: ' '; - display: table; } - .mh-calendar__date { - color: #666; - float: left; - font-size: 14px; - height: 40px; - line-height: 40px; - position: relative; - text-align: center; - width: 40px; } - .mh-calendar__date:hover { - cursor: pointer; } - .mh-calendar__date:hover::after { - background: rgba(226, 106, 106, 0.1); - border-radius: 20px; - content: ''; - display: block; - height: 40px; - left: 0; - position: absolute; - top: 0; - width: 40px; - z-index: -1; } - .mh-calendar__date--in-range { - background: rgba(226, 106, 106, 0.1); } - .mh-calendar__date--range-start { - background: #e26a6a; - border-bottom-left-radius: 20px; - border-top-left-radius: 20px; - color: #fff; - font-weight: bold; } - .mh-calendar__date--range-start:hover { - cursor: default; } - .mh-calendar__date--range-start:hover::after { - display: none; } - .mh-calendar__date--range-end { - background: #e26a6a; - border-bottom-right-radius: 20px; - border-top-right-radius: 20px; - color: #fff; - font-weight: bold; } - .mh-calendar__date--range-end:hover { - cursor: default; } - .mh-calendar__date--range-end:hover::after { - display: none; } - .mh-calendar__date--today { - text-decoration: underline; } - .mh-calendar__date--blocked { - color: #999; - font-style: italic; - font-weight: normal; } - .mh-calendar__date--blocked:hover { - cursor: default; } - .mh-calendar__date--blocked:hover::after { - display: none; } - .mh-calendar__date--range-start.mh-calendar__date--blocked, .mh-calendar__date--range-end.mh-calendar__date--blocked { - color: #fff; } - -.mh-date-picker, -.mh-date-range-picker { - background: #fff; - border: 1px solid #d4dce0; - border-radius: 3px; - margin-top: 10px; - position: absolute; - width: 300px; - z-index: 4; } - .mh-date-picker--closed, - .mh-date-range-picker--closed { - display: none; } - -.mh-date-range-picker { - width: 600px; } - .mh-date-range-picker .mh-calendar:first-child .mh-calendar__next { - display: none; } - .mh-date-range-picker .mh-calendar:nth-child(2) .mh-calendar__previous { - display: none; } diff --git a/coffee/dist/index.html b/coffee/dist/index.html deleted file mode 100644 index 9e3a7c2..0000000 --- a/coffee/dist/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - manhattan-date-picker - - - -
- - -
- - - - \ No newline at end of file diff --git a/coffee/dist/index.js b/coffee/dist/index.js deleted file mode 100644 index 211f6b7..0000000 --- a/coffee/dist/index.js +++ /dev/null @@ -1,1151 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["ManhattanDatePicker"] = factory(); - else - root["ManhattanDatePicker"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - __webpack_require__(1); - module.exports = __webpack_require__(2); - - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = __webpack_require__.p + "date-picker.css"; - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - var $, BasePicker, Calendar, DatePicker, DateRangePicker, - bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - $ = __webpack_require__(3); - - Calendar = __webpack_require__(4).Calendar; - - BasePicker = (function() { - function BasePicker() { - this.close = bind(this.close, this); - this._isOpen = false; - Object.defineProperty(this, 'isOpen', { - get: (function(_this) { - return function() { - return _this._isOpen; - }; - })(this) - }); - $.listen(window, { - 'fullscreenchange orientationchange resize': (function(_this) { - return function(ev) { - if (_this.isOpen) { - return _this.close('resize'); - } - }; - })(this) - }); - } - - BasePicker.prototype.close = function(input, block, reason) { - if (!this.isOpen) { - return; - } - this._dom.picker.classList.add(this._bem(block, '', 'closed')); - this._isOpen = false; - return $.dispatch(input, this._et('close'), { - 'reason': reason - }); - }; - - BasePicker.prototype._bem = function(block, element, modifier) { - var name; - if (element == null) { - element = ''; - } - if (modifier == null) { - modifier = ''; - } - name = block; - if (element) { - name = name + "__" + element; - } - if (modifier) { - name = name + "--" + modifier; - } - return name; - }; - - BasePicker.prototype._track = function(input) { - var left, rect, top; - rect = input.getBoundingClientRect(); - top = rect.top += window.scrollY; - left = rect.left += window.scrollX; - this._dom.picker.style.top = (top + rect.height) + "px"; - return this._dom.picker.style.left = left + "px"; - }; - - return BasePicker; - - })(); - - DatePicker = (function(superClass) { - extend(DatePicker, superClass); - - DatePicker.clsPrefix = 'data-mh-date-picker--'; - - function DatePicker(input, options) { - var eventListeners, proxyOptions; - if (options == null) { - options = {}; - } - this.open = bind(this.open, this); - DatePicker.__super__.constructor.call(this); - $.config(this, { - closeOnPick: false, - format: 'human_en', - parsers: ['human_en', 'dmy', 'iso'] - }, options, input, this.constructor.clsPrefix); - this._behaviours = {}; - $.config(this._behaviours, { - input: 'set-value' - }, options, input, this.constructor.clsPrefix); - this._dom = {}; - this._dom.input = input; - this._dom.input.__mh_datePicker = this; - this._dom.picker = $.create('div', { - 'class': [this._bem('mh-date-picker'), this._bem('mh-date-picker', '', 'closed')].join(' ') - }); - document.body.appendChild(this._dom.picker); - proxyOptions = Calendar.proxyOptions(options, input); - this._calendar = new Calendar(this._dom.picker, proxyOptions); - Object.defineProperty(this, 'calendar', { - get: (function(_this) { - return function() { - return _this._calendar; - }; - })(this) - }); - Object.defineProperty(this, 'input', { - value: this._dom.input - }); - $.listen(this.input, { - 'blur': (function(_this) { - return function() { - return _this.close('blur'); - }; - })(this), - 'click': (function(_this) { - return function() { - return _this.open(); - }; - })(this), - 'focus': (function(_this) { - return function() { - return _this.open(); - }; - })(this), - 'change': (function(_this) { - return function(ev) { - var date; - if (ev.caller === _this) { - return; - } - date = Calendar.parseDate(_this.input.value, _this.parsers); - if (date) { - return _this.pick(date, 'input'); - } - }; - })(this) - }); - eventListeners = {}; - eventListeners[this.calendar._et('pick')] = (function(_this) { - return function(ev) { - return _this.pick(ev.date, 'calendar'); - }; - })(this); - $.listen(this.calendar.calendar, eventListeners); - } - - DatePicker.prototype.close = function(reason) { - return DatePicker.__super__.close.call(this, this.input, 'mh-date-picker', reason); - }; - - DatePicker.prototype.open = function() { - var date; - date = Calendar.parseDate(this.input.value, this.parsers); - if (date) { - this.calendar.goto(date.getMonth(), date.getFullYear()); - this.calendar.select(date); - } - this._track(); - this._dom.picker.classList.remove(this._bem('mh-date-picker', '', 'closed')); - this._isOpen = true; - return $.dispatch(this.input, this._et('open')); - }; - - DatePicker.prototype.pick = function(date, source) { - if (source == null) { - source = ''; - } - this.calendar.select(date); - if ($.dispatch(this.input, this._et('pick'), { - 'date': date, - 'source': source - })) { - this.constructor.behaviours.input[this._behaviours.input](this, date); - $.dispatch(this.input, this._et('picked'), { - 'date': date, - 'source': source - }); - if (this.closeOnPick) { - return this.close({ - 'reason': 'pick' - }); - } - } - }; - - DatePicker.prototype._et = function(eventName) { - return "mh-date-picker--" + eventName; - }; - - DatePicker.prototype._track = function() { - return DatePicker.__super__._track.call(this, this.input); - }; - - DatePicker.behaviours = { - input: { - 'set-hidden': function(datePicker, date) { - var dateStr, hidden, hiddenDateStr, hiddenFormat, hiddenSelector; - dateStr = Calendar.formats[datePicker.format](date); - datePicker.input.value = dateStr; - hiddenSelector = datePicker.input.getAttribute(datePicker.constructor.clsPrefix + "hidden"); - hidden = $.one(hiddenSelector); - hiddenFormat = datePicker.input.getAttribute(datePicker.constructor.clsPrefix + "hidden-format"); - hiddenDateStr = Calendar.formats[hiddenFormat](date); - hidden.value = hiddenDateStr; - return $.dispatch(hidden, 'change'); - }, - 'set-value': function(datePicker, date) { - var dateStr; - dateStr = Calendar.formats[datePicker.format](date); - datePicker.input.value = dateStr; - $.dispatch(datePicker.input, 'change', { - caller: datePicker - }); - return console.log(dateStr); - } - } - }; - - return DatePicker; - - })(BasePicker); - - DateRangePicker = (function(superClass) { - extend(DateRangePicker, superClass); - - DateRangePicker.clsPrefix = 'data-mh-date-range-picker--'; - - function DateRangePicker(startInput, endInput, options) { - var eventListeners, i, input, len, proxyOptions, ref; - if (options == null) { - options = {}; - } - DateRangePicker.__super__.constructor.call(this); - $.config(this, { - closeOnPick: false, - format: 'human_en', - parsers: ['human_en', 'dmy', 'iso'], - pinToStart: false - }, options, startInput, this.constructor.clsPrefix); - this._behaviours = {}; - $.config(this._behaviours, { - input: 'set-value' - }, options, startInput, this.constructor.clsPrefix); - this._dom = {}; - this._dom.picker = $.create('div', { - 'class': [this._bem('mh-date-range-picker'), this._bem('mh-date-range-picker', '', 'closed')].join(' ') - }); - document.body.appendChild(this._dom.picker); - this._dom.startInput = startInput; - this._dom.startInput.__mh_dateRangePicker = this; - this._dom.endInput = endInput; - this._dom.endInput.__mh_dateRangePicker = this; - proxyOptions = Calendar.proxyOptions(options, startInput); - this._calendars = [new Calendar(this._dom.picker, proxyOptions), new Calendar(this._dom.picker, proxyOptions)]; - this._picking = 'start'; - Object.defineProperty(this, 'calendars', { - get: (function(_this) { - return function() { - return _this._calendars; - }; - })(this) - }); - Object.defineProperty(this, 'endInput', { - value: this._dom.endInput - }); - Object.defineProperty(this, 'startInput', { - value: this._dom.startInput - }); - Object.defineProperty(this, 'picking', { - get: (function(_this) { - return function() { - return _this._picking; - }; - })(this) - }); - ref = [this.startInput, this.endInput]; - for (i = 0, len = ref.length; i < len; i++) { - input = ref[i]; - $.listen(input, { - 'click': function(ev) { - return ev.target.focus(); - }, - 'focus': (function(_this) { - return function(ev) { - if (ev.target === _this.startInput) { - _this._picking = 'start'; - } else { - _this._picking = 'end'; - } - return _this.open(); - }; - })(this), - 'blur': (function(_this) { - return function() { - var activeInput; - activeInput = document.activeElement; - if (_this.startInput === activeInput || _this.endInput === activeInput) { - return; - } - return _this.close('blur'); - }; - })(this), - 'change': (function(_this) { - return function(ev) { - var date, dateRange; - if (ev.caller === _this) { - return; - } - date = Calendar.parseDate(ev.target.value, _this.parsers); - if (date) { - dateRange = _this.calendars[0].dateRange; - if (_this.picking === 'start') { - dateRange[0] = date; - } else { - dateRange[1] = date; - } - return _this.pick(dateRange, { - 'source': 'input' - }); - } - }; - })(this) - }); - } - eventListeners = {}; - eventListeners[this.calendars[0]._et('pick')] = (function(_this) { - return function(ev) { - var dateRange; - dateRange = _this.calendars[0].dateRange; - if (_this.picking === 'start') { - dateRange[0] = ev.date; - } else { - dateRange[1] = ev.date; - } - return _this.pick(dateRange, { - 'source': 'calendar' - }); - }; - })(this); - eventListeners[this.calendars[0]._et('view')] = (function(_this) { - return function(ev) { - if (_this.calendars.indexOf(ev.calendar) === 0) { - return _this.calendars[1].sync(_this.calendars[0], 1); - } else { - return _this.calendars[0].sync(_this.calendars[1], -1); - } - }; - })(this); - $.listen(this.calendars[0].calendar, eventListeners); - $.listen(this.calendars[1].calendar, eventListeners); - } - - DateRangePicker.prototype.close = function(reason) { - return DateRangePicker.__super__.close.call(this, this.startInput, 'mh-date-range-picker', reason); - }; - - DateRangePicker.prototype.open = function() { - var calendar, closedClass, dateRange, endDate, endStr, i, input, len, ref, startDate, startStr, viewDate, viewStrs; - input = this.startInput; - if (this.picking === 'end') { - input = this.endInput; - } - startDate = Calendar.parseDate(this.startInput.value, this.parsers); - endDate = Calendar.parseDate(this.endInput.value, this.parsers); - dateRange = this.calendars[0].dateRange; - if (startDate) { - dateRange[0] = startDate; - } - if (endDate) { - dateRange[1] = endDate; - } - if (dateRange[1] < dateRange[0]) { - this.pick([dateRange[1], dateRange[0]]); - this.endInput.focus(); - return; - } - ref = this.calendars; - for (i = 0, len = ref.length; i < len; i++) { - calendar = ref[i]; - calendar.select(dateRange[0], dateRange[1]); - } - startStr = (dateRange[0].getMonth()) + "." + (dateRange[0].getFullYear()); - endStr = (dateRange[1].getMonth()) + "." + (dateRange[1].getFullYear()); - viewStrs = [this.calendars[0].month + "." + this.calendars[0].year, this.calendars[1].month + "." + this.calendars[1].year]; - if (viewStrs.indexOf(startStr) === -1 && viewStrs.indexOf(endStr) === -1) { - viewDate = dateRange[0]; - this.calendars[0].goto(viewDate.getMonth(), viewDate.getFullYear()); - } - this._track(this.pinToStart ? this.startInput : input); - closedClass = this._bem('mh-date-range-picker', '', 'closed'); - this._dom.picker.classList.remove(closedClass); - this._isOpen = true; - return $.dispatch(this.startInput, this._et('open')); - }; - - DateRangePicker.prototype.pick = function(dateRange, source) { - var calendar, evData, i, len, ref; - ref = this.calendars; - for (i = 0, len = ref.length; i < len; i++) { - calendar = ref[i]; - calendar.select(dateRange[0], dateRange[1]); - } - evData = { - 'dateRange': dateRange, - 'source': source - }; - if ($.dispatch(this.startInput, this._et('pick'), evData)) { - this.constructor.behaviours.input[this._behaviours.input](this, dateRange); - $.dispatch(this.startInput, this._et('picked'), evData); - if (this.picking === 'start') { - this.endInput.focus(); - } else { - this.startInput.focus(); - } - if (this.closeOnPick) { - return this.close(); - } - } - }; - - DateRangePicker.prototype._et = function(eventName) { - return "mh-date-range-picker--" + eventName; - }; - - DateRangePicker.behaviours = { - input: { - 'set-value': function(dateRangePicker, dateRange) { - var format; - format = Calendar.formats[dateRangePicker.format]; - dateRangePicker.startInput.value = format(dateRange[0]); - $.dispatch(dateRangePicker.startInput, 'change', { - caller: datePicker - }); - dateRangePicker.endInput.value = format(dateRange[1]); - return $.dispatch(dateRangePicker.endInput, 'change', { - caller: datePicker - }); - } - } - }; - - return DateRangePicker; - - })(BasePicker); - - module.exports = { - Calendar: Calendar, - DatePicker: DatePicker, - DateRangePicker: DateRangePicker - }; - - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - !function(e,t){ true?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ManhattanEssentials=t():e.ManhattanEssentials=t()}(this,function(){return function(e){function __webpack_require__(r){if(t[r])return t[r].exports;var n=t[r]={exports:{},id:r,loaded:!1};return e[r].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}var t={};return __webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.p="",__webpack_require__(0)}([function(e,t,r){e.exports=r(1)},function(e,t){var r,n,u,o,i,a,c,s,p=[].indexOf||function(e){for(var t=0,r=this.length;t=0?r[n]=u:r.setAttribute(n,u);return r},c=function(e,t){return null==t&&(t=document),Array.prototype.slice.call(t.querySelectorAll(e))},s=function(e,t){return null==t&&(t=document),t.querySelector(e)},u=function(e,t,r){var n,u,o;null==r&&(r={}),n=document.createEvent("Event"),n.initEvent(t,!0,!0);for(u in r)o=r[u],n[u]=o;return e.dispatchEvent(n)},i=function(e,t){var r,n,u,o;o=[];for(n in t)u=t[n],o.push(function(){var t,o,i,a;for(i=n.split(/\s+/),a=[],t=0,o=i.length;t ref1; i = ref <= ref1 ? ++j : --j) { - weekday = $.create('div', { - 'class': this._bem('mh-calendar', 'weekday') - }); - weekdayIndex = i; - if (i >= this.weekdayNames.length) { - weekdayIndex -= this.weekdayNames.length; - } - weekday.innerHTML = this.weekdayNames[weekdayIndex]; - this._dom.weekdays.appendChild(weekday); - } - this._dom.dates = $.create('div', { - 'class': this._bem('mh-calendar', 'dates') - }); - this._dom.calendar.appendChild(this._dom.dates); - for (i = k = 0, ref2 = 7 * 6; 0 <= ref2 ? k < ref2 : k > ref2; i = 0 <= ref2 ? ++k : --k) { - date = $.create('div', { - 'class': this._bem('mh-calendar', 'date') - }); - this._dom.dates.appendChild(date); - } - Object.defineProperty(this, 'calendar', { - value: this._dom.calendar - }); - Object.defineProperty(this, 'parent', { - value: this._dom.parent - }); - Object.defineProperty(this, 'dateRange', { - get: function() { - return this._dateRange.slice(); - } - }); - Object.defineProperty(this, 'month', { - get: function() { - return this._month; - } - }); - Object.defineProperty(this, 'year', { - get: function() { - return this._year; - } - }); - $.listen(this._dom.calendar, { - 'mousedown': function(ev) { - return ev.preventDefault(); - } - }); - $.listen(this._dom.next, { - 'click': (function(_this) { - return function(ev) { - ev.preventDefault(); - return _this.next(); - }; - })(this) - }); - $.listen(this._dom.previous, { - 'click': (function(_this) { - return function(ev) { - ev.preventDefault(); - return _this.previous(); - }; - })(this) - }); - $.listen(this._dom.dates, { - 'click': (function(_this) { - return function(ev) { - var blockedCSS, dateElm; - ev.preventDefault(); - dateElm = ev.target; - if (dateElm === _this._dom.dates) { - return; - } - while (dateElm.parentNode !== _this._dom.dates) { - dateElm = dateElm.parentNode; - } - blockedCSS = _this._bem('mh-calendar', 'date', 'blocked'); - if (dateElm.classList.contains(blockedCSS)) { - return; - } - return $.dispatch(_this.calendar, _this._et('pick'), { - 'calendar': _this, - 'date': dateElm.__mh_date - }); - }; - })(this) - }); - this.update(); - } - - Calendar.prototype.destroy = function() { - if (this.calendar.parentNode === this.parent) { - return this.parent.removeChild(this.calendar); - } - }; - - Calendar.prototype.goto = function(month, year) { - if (this._month === month && year === this._year) { - return; - } - this._month = month; - this._year = year; - this.update(); - return $.dispatch(this.calendar, this._et('view'), { - 'calendar': this, - 'month': month, - 'year': year - }); - }; - - Calendar.prototype.next = function() { - return this.offset(1); - }; - - Calendar.prototype.offset = function(months, years) { - var month, year; - if (years == null) { - years = 0; - } - if (months > 0) { - years += Math.floor(Math.abs(months) / 12); - } else { - years -= Math.floor(Math.abs(months) / 12); - } - months = months % 12; - month = this._month + months; - year = this._year + years; - if (month < 0) { - month = 12 + month; - year -= 1; - } else if (month > 11) { - month = month - 12; - year += 1; - } - return this.goto(month, year); - }; - - Calendar.prototype.previous = function() { - return this.offset(-1); - }; - - Calendar.prototype.select = function(startDate, endDate) { - if (endDate == null) { - endDate = null; - } - if (endDate) { - this._dateRange = [startDate, endDate]; - } else { - this._dateRange = [startDate, startDate]; - } - return this.update(); - }; - - Calendar.prototype.sync = function(otherCalendar, months, years) { - var month, year; - if (years == null) { - years = 0; - } - if (months > 0) { - years += Math.floor(Math.abs(months) / 12); - } else { - years -= Math.floor(Math.abs(months) / 12); - } - months = months % 12; - month = otherCalendar.month + months; - year = otherCalendar.year + years; - if (month < 0) { - month = 12 + month; - year -= 1; - } else if (month > 11) { - month = month - 12; - year += 1; - } - if (this._month === month && year === this._year) { - return; - } - this._month = month; - this._year = year; - return this.update(); - }; - - Calendar.prototype.update = function() { - var classList, date, dateElm, daysOffset, i, j, ref, results, test, today, weekday; - this._dom.month.innerHTML = this.monthNames[this.month] + ", " + this.year; - weekday = new Date(this.year, this.month, 1).getDay(); - console.log(weekday, this.firstWeekday); - daysOffset = weekday - this.firstWeekday; - if (daysOffset < 0) { - daysOffset = 7 - Math.abs(daysOffset); - } - date = new Date(this.year, this.month, 1); - if (daysOffset > 0) { - date.setDate(date.getDate() - daysOffset); - } - results = []; - for (i = j = 0, ref = 7 * 6; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { - dateElm = this._dom.dates.childNodes[i]; - dateElm.innerHTML = date.getDate(); - dateElm.__mh_date = new Date(date); - dateElm.setAttribute('class', this._bem('mh-calendar', 'date')); - classList = dateElm.classList; - if (date.getMonth() !== this.month) { - classList.add(this._bem('mh-calendar', 'date', 'blocked')); - } - if (this.minDate && this.minDate.getTime() > date.getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'blocked')); - } - if (this.maxDate && this.maxDate.getTime() < date.getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'blocked')); - } - test = this.constructor.behaviours.test[this._behaviours.test]; - if (!test(this, this.dates, date)) { - classList.add(this._bem('mh-calendar', 'date', 'blocked')); - } - today = new Date(); - today.setHours(0, 0, 0, 0); - if (date.getTime() === today.getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'today')); - } - if (date.getTime() === this.dateRange[0].getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'range-start')); - } - if (date.getTime() === this.dateRange[1].getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'range-end')); - } - if (date.getTime() > this.dateRange[0].getTime() && date.getTime() < this.dateRange[1].getTime()) { - classList.add(this._bem('mh-calendar', 'date', 'in-range')); - } - results.push(date.setDate(date.getDate() + 1)); - } - return results; - }; - - Calendar.prototype._bem = function(block, element, modifier) { - var name; - if (element == null) { - element = ''; - } - if (modifier == null) { - modifier = ''; - } - name = block; - if (element) { - name = name + "__" + element; - } - if (modifier) { - name = name + "--" + modifier; - } - return name; - }; - - Calendar.prototype._et = function(eventName) { - return "mh-calendar--" + eventName; - }; - - Calendar.parseDate = function(s, parsers) { - var date, j, len, parser; - if (s instanceof Date) { - return s; - } - for (j = 0, len = parsers.length; j < len; j++) { - parser = parsers[j]; - date = this.parsers[parser](s); - if (date) { - return date; - } - } - }; - - Calendar.proxyOptions = function(prefix, options, input) { - var _parse, _split, defaults, j, len, option, proxy, ref, v; - defaults = Calendar.getDefaultConfig(); - defaults.parsers = []; - proxy = {}; - $.config(proxy, defaults, options, input, prefix); - _parse = function(s, parsers) { - return Calendar.parseDate(s, parsers); - }; - _split = function(s) { - var v; - return (function() { - var j, len, ref, results; - ref = s.split(','); - results = []; - for (j = 0, len = ref.length; j < len; j++) { - v = ref[j]; - if (v.trim()) { - results.push(v.trim()); - } - } - return results; - })(); - }; - if (typeof proxy.firstWeekday === 'string') { - proxy.firstWeekday = Number(proxy.firstWeekday); - } - ref = ['dates', 'monthNames', 'parsers', 'weekdayNames']; - for (j = 0, len = ref.length; j < len; j++) { - option = ref[j]; - if (typeof proxy[option] === 'string') { - proxy[option] = _split(proxy[option]); - } - } - if (typeof proxy.minDate === 'string') { - proxy.minDate = _parse(proxy.minDate, proxy.parsers); - } - if (typeof proxy.maxDate === 'string') { - proxy.maxDate = _parse(proxy.maxDate, proxy.parsers); - } - proxy.dates = (function() { - var k, len1, ref1, results; - ref1 = proxy.dates; - results = []; - for (k = 0, len1 = ref1.length; k < len1; k++) { - v = ref1[k]; - if (_parse(v, proxy.parsers)) { - results.push(_parse(v, proxy.parsers)); - } - } - return results; - })(); - delete proxy.parsers; - return proxy; - }; - - Calendar.behaviours = { - test: { - 'any': function(calendar, dates, date) { - return true; - }, - 'excluding': function(calendar, dates, date) { - var j, len, other_date; - for (j = 0, len = dates.length; j < len; j++) { - other_date = dates[j]; - if (date.getTime() === other_date.getTime()) { - return false; - } - } - return true; - }, - 'only': function(calendar, dates, date) { - var j, len, other_date; - for (j = 0, len = dates.length; j < len; j++) { - other_date = dates[j]; - if (date.getTime() === other_date.getTime()) { - return true; - } - } - return false; - }, - 'weekdays': function(calendar, weekdays, date) { - return weekdays.indexOf(date.getDay()) > -1; - } - } - }; - - Calendar.formats = { - 'dmy': function(date) { - var dd, mm, yyyy; - dd = ("00" + (date.getDate())).slice(-2); - mm = ("00" + (date.getMonth() + 1)).slice(-2); - yyyy = date.getFullYear(); - return dd + "/" + mm + "/" + yyyy; - }, - 'human_en': function(date) { - var month_name; - month_name = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][date.getMonth()]; - return (date.getDate()) + " " + month_name + " " + (date.getFullYear()); - }, - 'human_abbr_en': function(date) { - var month_name; - month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()]; - return (date.getDate()) + " " + month_name + " " + (date.getFullYear()); - }, - 'iso': function(date) { - var dd, mm, yyyy; - dd = ("00" + (date.getDate())).slice(-2); - mm = ("00" + (date.getMonth() + 1)).slice(-2); - yyyy = date.getFullYear(); - return yyyy + "-" + mm + "-" + dd; - }, - 'mdy': function(date) { - var dd, mm, yyyy; - dd = ("00" + (date.getDate())).slice(-2); - mm = ("00" + (date.getMonth() + 1)).slice(-2); - yyyy = date.getFullYear(); - return mm + "/" + dd + "/" + yyyy; - } - }; - - Calendar.parsers = { - 'dmy': function(s) { - var date, dateExp, day, match, month, year; - dateExp = /^(\d{1,2})(\/|\.|\-)(\d{1,2})(\/|\.|\-)(\d{2}|\d{4})$/; - match = dateExp.exec(s); - if (!match) { - return; - } - year = Number(match[5]); - month = Number(match[3]) - 1; - day = Number(match[1]); - if (year < 100) { - year += parseInt((new Date()).getFullYear() / 100) * 100; - } - date = new Date(year, month, day); - if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) { - return; - } - return date; - }, - 'human_en': function(s) { - var component, components, date, day, i, j, k, len, len1, month, month_names, month_short_names, ref, ref1, year; - month_names = { - 'january': 0, - 'february': 1, - 'march': 2, - 'april': 3, - 'may': 4, - 'june': 5, - 'july': 6, - 'august': 7, - 'september': 8, - 'october': 9, - 'november': 10, - 'december': 11 - }; - month_short_names = { - 'jan': 0, - 'feb': 1, - 'mar': 2, - 'apr': 3, - 'may': 4, - 'jun': 5, - 'jul': 6, - 'aug': 7, - 'sep': 8, - 'oct': 9, - 'nov': 10, - 'dec': 11 - }; - s = s.toLowerCase(); - s = s.replace(',', ' '); - s = s.replace(/(\d)st/g, '$1 '); - s = s.replace(/(\d)nd/g, '$1 '); - s = s.replace(/(\d)rd/g, '$1 '); - s = s.replace(/(\d)th/g, '$1 '); - s = s.trim(); - components = s.split(/\s+/); - if (components.length > 3) { - return; - } - month = (new Date()).getMonth(); - year = (new Date()).getFullYear(); - if (components.length > 1) { - month = null; - ref = components.slice(); - for (i = j = 0, len = ref.length; j < len; i = ++j) { - component = ref[i]; - if (month_names.hasOwnProperty(component)) { - month = month_names[component]; - } else if (month_short_names.hasOwnProperty(component)) { - month = month_short_names[component]; - } - if (month !== null) { - components.splice(i, 1); - break; - } - } - if (month === null) { - return; - } - year = null; - ref1 = components.slice(); - for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) { - component = ref1[i]; - if (component.length === 4) { - year = Number(component); - components.splice(i, 1); - break; - } - } - if (year === null && components.length === 2) { - year = Number(components[1]); - components.splice(1, 1); - } - if (year === (0/0)) { - return; - } - if (year === null) { - year = (new Date()).getFullYear(); - } - if (year < 100) { - year += parseInt((new Date()).getFullYear() / 100) * 100; - } - } - day = Number(components[0]); - if (day === (0/0)) { - return; - } - date = new Date(year, month, day); - if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) { - return; - } - return date; - }, - 'iso': function(s) { - var date, dateExp, day, match, month, year; - dateExp = /^(\d{4})-(\d{2})-(\d{2})$/; - match = dateExp.exec(s); - if (!match) { - return; - } - year = Number(match[1]); - month = Number(match[2]) - 1; - day = Number(match[3]); - date = new Date(year, month, day); - if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) { - return; - } - return date; - }, - 'mdy': function(s) { - var date, dateExp, day, match, month, year; - dateExp = /^(\d{1,2})(\/|\.|\-)(\d{1,2})(\/|\.|\-)(\d{2}|\d{4})$/; - match = dateExp.exec(s); - if (!match) { - return; - } - year = Number(match[5]); - month = Number(match[1]) - 1; - day = Number(match[3]); - if (year < 100) { - year += parseInt((new Date()).getFullYear() / 100) * 100; - } - date = new Date(year, month, day); - if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) { - return; - } - return date; - } - }; - - return Calendar; - - })(); - - module.exports = { - Calendar: Calendar - }; - - -/***/ } -/******/ ]) -}); -; \ No newline at end of file diff --git a/coffee/package.json b/coffee/package.json deleted file mode 100644 index ae20b65..0000000 --- a/coffee/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "manhattan-date-picker", - "version": "0.0.7", - "description": "Date picker support for form fields.", - "main": "dist/index.js", - "scripts": { - "dist": "NODE_ENV=dist webpack --progress", - "live": "webpack-dev-server --progress", - "watch": "webpack --watch --progress", - "test": "mocha --require coffee-script/register --compilers coffee:coffee-script" - }, - "repository": { - "type": "git", - "url": "git@git.getme.co.uk:manhattan/manhattan_js_date-picker.git" - }, - "keywords": [ - "typeahead", - "manhattan" - ], - "author": "Anthony Blackshaw", - "license": "MIT", - "devDependencies": { - "chai": "^3.5.0", - "coffee-loader": "^0.7.2", - "coffee-script": "^1.11.1", - "coffeelint": "^1.16.0", - "coffeelint-loader": "^0.1.1", - "css-loader": "^0.25.0", - "extract-loader": "0.0.2", - "file-loader": "^0.9.0", - "jsdom": "^9.8.3", - "manhattan-essentials": "0.0.10", - "mocha": "^3.1.2", - "mocha-jsdom": "^1.1.0", - "node-sass": "^3.11.1", - "sass-loader": "^4.0.2", - "sasslint-loader": "0.0.1", - "sinon": "^1.17.6", - "sinon-chai": "^2.8.0", - "webpack": "^1.13.3", - "webpack-dev-server": "^1.16.2" - } -} diff --git a/coffee/src/scripts/calendar.coffee b/coffee/src/scripts/calendar.coffee deleted file mode 100644 index 0e8d4f2..0000000 --- a/coffee/src/scripts/calendar.coffee +++ /dev/null @@ -1,733 +0,0 @@ -$ = require 'manhattan-essentials' - - -class Calendar - - # A calendar from which a date can be selected. - - @getDefaultConfig: () -> - # Return the default configuration options for the `Calendar` class - return { - # Typically a list of dates, the `dates` option is used in - # conjunction with the `test` behaviour to determine which dates - # can be picked. - dates: [], - - # The earliest date that can be selected - minDate: null, - - # The latest date that can be selected - maxDate: null, - - # The weekday that will be displayed first in the calendar view. - # Weekday must be a number between 0 (Sunday) and 6 (Saturday). - firstWeekday: 1, - - # A list of all month names that will be used when displaying - # the month (must contain exactly 12 names). - monthNames: [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December' - ], - - # A list of weekday names that will be used when displaying the - # days of the week (must contain exactly 7 names). - weekdayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] - } - - constructor: (parent, options={}) -> - - # Configure the instance - $.config(this, Calendar.getDefaultConfig(), options) - - # Set up and configure behaviours - @_behaviours = {} - $.config(@_behaviours, {test: 'any'}, options) - - # Initially the calendar view will focus on today's date - today = new Date() - today.setHours(0, 0, 0, 0) - - # The date range currently selected - @_dateRange = [today, today] - - # The month and year currently displayed by the calendar - @_month = today.getMonth() - @_year = today.getFullYear() - - # Domain for related DOM elements - @_dom = {} - - # Store a reference to the parent the calendar is being added to - @_dom.parent = parent - - # Build the elements required for the calendar - - # Calendar - @_dom.calendar = $.create('div', {'class': @_bem('mh-calendar')}) - parent.appendChild(@_dom.calendar) - - # Nav - @_dom.nav = $.create('div', {'class': @_bem('mh-calendar', 'nav')}) - @_dom.calendar.appendChild(@_dom.nav) - - @_dom.month = $.create('div', {'class': @_bem('mh-calendar', 'month')}) - @_dom.nav.appendChild(@_dom.month) - - @_dom.next = $.create( - 'div', {'class': @_bem('mh-calendar', 'next')}) - @_dom.previous = $.create( - 'div', {'class': @_bem('mh-calendar', 'previous')}) - @_dom.nav.appendChild(@_dom.next) - @_dom.nav.appendChild(@_dom.previous) - - # Weekdays - @_dom.weekdays = $.create( - 'div', {'class': @_bem('mh-calendar', 'weekdays')}) - @_dom.calendar.appendChild(@_dom.weekdays) - - for i in [@firstWeekday...(@firstWeekday + 7)] - weekday = $.create( - 'div', {'class': @_bem('mh-calendar', 'weekday')}) - - weekdayIndex = i - if i >= @weekdayNames.length - weekdayIndex -= @weekdayNames.length - weekday.innerHTML = @weekdayNames[weekdayIndex] - - @_dom.weekdays.appendChild(weekday) - - # Dates - @_dom.dates = $.create('div', {'class': @_bem('mh-calendar', 'dates')}) - @_dom.calendar.appendChild(@_dom.dates) - - for i in [0...(7 * 6)] - date = $.create('div', {'class': @_bem('mh-calendar', 'date')}) - @_dom.dates.appendChild(date) - - # Define read-only properties - Object.defineProperty(this, 'calendar', {value: @_dom.calendar}) - Object.defineProperty(this, 'parent', {value: @_dom.parent}) - Object.defineProperty( - this, 'dateRange', {get: () -> return @_dateRange.slice()}) - Object.defineProperty(this, 'month', {get: () -> return @_month}) - Object.defineProperty(this, 'year', {get: () -> return @_year}) - - # Set up event listeners for the calendar - $.listen @_dom.calendar, - 'mousedown': (ev) -> - # Prevent picking from the calendar switching focus - ev.preventDefault() - - $.listen @_dom.next, - 'click': (ev) => - ev.preventDefault() - @next() - - $.listen @_dom.previous, - 'click': (ev) => - ev.preventDefault() - @previous() - - $.listen @_dom.dates, - 'click': (ev) => - ev.preventDefault() - - # Find the date element - dateElm = ev.target - - if dateElm is @_dom.dates - return - - while dateElm.parentNode isnt @_dom.dates - dateElm = dateElm.parentNode - - # Check the date isn't been blocked - blockedCSS = @_bem('mh-calendar', 'date', 'blocked') - if dateElm.classList.contains(blockedCSS) - return - - # Dispatch a pick event against the calendar - $.dispatch( - @calendar, - @_et('pick'), - {'calendar': this, 'date': dateElm.__mh_date} - ) - - # Update the calendar view - @update() - - # Public methods - - destroy: () => - # Destroy the calendar (remove it from the DOM) - if @calendar.parentNode == @parent - @parent.removeChild(@calendar) - - goto: (month, year) => - # Display the given month, year in the calendar - - # If the month and year match then there's nothing to do - if @_month == month and year == @_year - return - - # Update the view - @_month = month - @_year = year - @update() - - # Dispatch a goto event against the calendar - $.dispatch( - @calendar, - @_et('view'), - {'calendar': this, 'month': month, 'year': year} - ) - - next: () -> - # Display the next month in the calendar - @offset(1) - - offset: (months, years=0) -> - # Display the given offset (from the current month, year) in the - # calendar. - - # Handle month offsets in excess of 1 year (12 months) - if months > 0 - years += Math.floor(Math.abs(months) / 12) - else - years -= Math.floor(Math.abs(months) / 12) - months = months % 12 - - # Calculate the new month, year - month = @_month + months - year = @_year + years - - # Handle rotating the year when the month value is < 0 or > 11 - if month < 0 - month = 12 + month - year -= 1 - - else if month > 11 - month = month - 12 - year += 1 - - # Apply the offset - @goto(month, year) - - previous: () -> - # Display the previous month in the calendar - @offset(-1) - - select: (startDate, endDate=null) -> - # Set the selected date/date range for the calendar - if endDate - @_dateRange = [startDate, endDate] - else - @_dateRange = [startDate, startDate] - - @update() - - sync: (otherCalendar, months, years=0) -> - # Sync this calendar view with another with the given offset - - # Handle month offsets in excess of 1 year (12 months) - if months > 0 - years += Math.floor(Math.abs(months) / 12) - else - years -= Math.floor(Math.abs(months) / 12) - months = months % 12 - - # Calculate the new month, year - month = otherCalendar.month + months - year = otherCalendar.year + years - - # Handle rotating the year when the month value is < 0 or > 11 - if month < 0 - month = 12 + month - year -= 1 - - else if month > 11 - month = month - 12 - year += 1 - - # If the month and year match then there's nothing to do - if @_month == month and year == @_year - return - - # Update the view - @_month = month - @_year = year - @update() - - update: () -> - # Update the calendar based on the current date range - - # Update the month, year - @_dom.month.innerHTML = "#{@monthNames[@month]}, #{@year}" - - # Find the first weekday in the month - weekday = new Date(@year, @month, 1).getDay() - - # Determine the start date for the calendar month given the the first - # day. - daysOffset = weekday - @firstWeekday - if daysOffset < 0 - daysOffset = 7 - Math.abs(daysOffset) - - date = new Date(@year, @month, 1) - if daysOffset > 0 - date.setDate(date.getDate() - daysOffset) - - # Update the dates - for i in [0...(7 * 6)] - dateElm = @_dom.dates.childNodes[i] - - # Set the contents of the date - dateElm.innerHTML = date.getDate() - - # Associate the JS date with the element - dateElm.__mh_date = new Date(date) - - # Clear all existing classes for the date - dateElm.setAttribute('class', @_bem('mh-calendar', 'date')) - - # Add any modified classes to the date - classList = dateElm.classList - - # Outside of the current months scope (blocked) - if date.getMonth() != @month - classList.add(@_bem('mh-calendar', 'date', 'blocked')) - - # Check if the date is within the min/max range - if @minDate and @minDate.getTime() > date.getTime() - classList.add(@_bem('mh-calendar', 'date', 'blocked')) - - if @maxDate and @maxDate.getTime() < date.getTime() - classList.add(@_bem('mh-calendar', 'date', 'blocked')) - - # Check if the date is blocked - test = @constructor.behaviours.test[@_behaviours.test] - if not test(this, @dates, date) - classList.add(@_bem('mh-calendar', 'date', 'blocked')) - - # Today's date - today = new Date() - today.setHours(0, 0, 0, 0) - if date.getTime() is today.getTime() - classList.add(@_bem('mh-calendar', 'date', 'today')) - - # Selected date - if date.getTime() is @dateRange[0].getTime() - classList.add(@_bem('mh-calendar', 'date', 'range-start')) - - if date.getTime() is @dateRange[1].getTime() - classList.add(@_bem('mh-calendar', 'date', 'range-end')) - - if date.getTime() > @dateRange[0].getTime() and - date.getTime() < @dateRange[1].getTime() - classList.add(@_bem('mh-calendar', 'date', 'in-range')) - - # Increment the date by one day - date.setDate(date.getDate() + 1) - - # Private methods - - _bem: (block, element='', modifier='') -> - # Build and return a class name - name = block - if element - name = "#{name}__#{element}" - if modifier - name = "#{name}--#{modifier}" - return name - - _et: (eventName) -> - # Generate an event type name - return "mh-calendar--#{eventName}" - - # Class methods - - @parseDate: (s, parsers) -> - # Parse a date string (if a date is supplied it'll be returned as is) - - # Check if we've been sent a date - if s instanceof Date - return s - - # Attempt to parse the string as a date - for parser in parsers - date = @parsers[parser](s) - if date - return date - - @proxyOptions: (prefix, options, input) -> - # Return a set of config options for the calendar based on a set of user - # defined options and an input element. - # - # The `proxyOptions` method is typically used by classes using one or - # more instances of the `Calendar` class and wishing to configure it - # through their own config options and input. - - # We add a default empty list of date parsers to the proxy to allow this - # to be configured by the caller. - defaults = Calendar.getDefaultConfig() - defaults.parsers = [] - - # Initially we configure a set of proxy options - proxy = {} - $.config(proxy, defaults, options, input, prefix) - - # Data options passed as strings need to be converted to native JS types - - _parse = (s, parsers) -> - # Attempt to parse a string to a date - return Calendar.parseDate(s, parsers) - - _split = (s) -> - # Convert a comma separated list of values to a native list - return (v.trim() for v in s.split(',') when v.trim()) - - # Convert numbers - if typeof(proxy.firstWeekday) is 'string' - proxy.firstWeekday = Number(proxy.firstWeekday) - - # Convert comma separated strings to native lists - for option in ['dates', 'monthNames', 'parsers', 'weekdayNames'] - if typeof(proxy[option]) is 'string' - proxy[option] = _split(proxy[option]) - - # Convert date strings to native dates - if typeof(proxy.minDate) is 'string' - proxy.minDate = _parse(proxy.minDate, proxy.parsers) - - if typeof(proxy.maxDate) is 'string' - proxy.maxDate = _parse(proxy.maxDate, proxy.parsers) - - proxy.dates = (_parse(v, proxy.parsers) for v in proxy.dates \ - when _parse(v, proxy.parsers)) - - # Remove parsers from the proxy - delete proxy.parsers - - return proxy - - # Behaviours - - @behaviours: - - test: - 'any': (calendar, dates, date) -> - # Allow any date - return true - - 'excluding': (calendar, dates, date) -> - # Only enable dates which are not in the list of dates - for other_date in dates - if date.getTime() is other_date.getTime() - return false - return true - - 'only': (calendar, dates, date) -> - # Only enable dates which are in the list of dates - for other_date in dates - if date.getTime() is other_date.getTime() - return true - return false - - 'weekdays': (calendar, weekdays, date) -> - # Only enable dates for the given list of weekdays - return weekdays.indexOf(date.getDay()) > -1 - - # Formats - - @formats: - - 'dmy': (date) -> - # Format a date as `dd/mm/yyyy` - dd = "00#{date.getDate()}".slice(-2) - mm = "00#{date.getMonth() + 1}".slice(-2) - yyyy = date.getFullYear() - return "#{dd}/#{mm}/#{yyyy}" - - 'human_en': (date) -> - # Format a date as `{day} {full month name} {full year}`, e.g - # `22 January 2011`. - month_name = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December' - ][date.getMonth()] - return "#{date.getDate()} #{month_name} #{date.getFullYear()}" - - 'human_abbr_en': (date) -> - # Format a date as `{day} {month name} {full year}`, e.g: - # - # - `22 Jan 2016` - month_name = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec' - ][date.getMonth()] - return "#{date.getDate()} #{month_name} #{date.getFullYear()}" - - 'iso': (date) -> - # Format a date as ISO 8601 (e.g `yyyy-mm-dd`) - dd = "00#{date.getDate()}".slice(-2) - mm = "00#{date.getMonth() + 1}".slice(-2) - yyyy = date.getFullYear() - return "#{yyyy}-#{mm}-#{dd}" - - 'mdy': (date) -> - # Format a date as `mm/dd/yyyy` - dd = "00#{date.getDate()}".slice(-2) - mm = "00#{date.getMonth() + 1}".slice(-2) - yyyy = date.getFullYear() - return "#{mm}/#{dd}/#{yyyy}" - - # Parsers - - @parsers: - - 'dmy': (s) -> - # Return a date from a string using the format (`d/m/y`), the slash - # separator can be a `/`, '-', or '.' - - # Parse the date information - dateExp = /^(\d{1,2})(\/|\.|\-)(\d{1,2})(\/|\.|\-)(\d{2}|\d{4})$/ - match = dateExp.exec(s) - if not match - return - - # Generate a date - year = Number(match[5]) - month = Number(match[3]) - 1 - day = Number(match[1]) - - # If the year doesn't contain the century then we add the current - # century to it. - if year < 100 - year += parseInt((new Date()).getFullYear() / 100) * 100 - - date = new Date(year, month, day) - - # Validate the date is as expected - if date.getFullYear() isnt year or - date.getMonth() isnt month or - date.getDate() isnt day - return - - return date - - 'human_en': (s) -> - # Return a date from a string using a human readable format, e.g: - # - # - `2nd` - # - `1 Aug` - # - `15 Feb 17` - # - `January 22, 2011` - # - `Nov 22` - # - # The order of the date components is not important, one component - # be a string representing the month, two other components must be - # digits (numbers) representing the month and year. - - # Define the name of the months in english - month_names = { - 'january': 0, - 'february': 1, - 'march': 2, - 'april': 3, - 'may': 4, - 'june': 5, - 'july': 6, - 'august': 7, - 'september': 8, - 'october': 9, - 'november': 10, - 'december': 11 - } - - month_short_names = { - 'jan': 0, - 'feb': 1, - 'mar': 2, - 'apr': 3, - 'may': 4, - 'jun': 5, - 'jul': 6, - 'aug': 7, - 'sep': 8, - 'oct': 9, - 'nov': 10, - 'dec': 11 - } - - # Clean up the date string removing commas as suffixes - s = s.toLowerCase() - s = s.replace(',', ' ') - s = s.replace(/(\d)st/g, '$1 ') - s = s.replace(/(\d)nd/g, '$1 ') - s = s.replace(/(\d)rd/g, '$1 ') - s = s.replace(/(\d)th/g, '$1 ') - s = s.trim() - - # Split the date string into its components - components = s.split(/\s+/) - - # Check there is not more than 3 components - if components.length > 3 - return - - # If there's only one component then this must be the day so we - # default the year and month to the current until we determine the - # number of components. - month = (new Date()).getMonth() - year = (new Date()).getFullYear() - - # If we're dealing with more than one component we need to attempt - # to extract the month (2+ components) and year (3 components). - if components.length > 1 - - # Find the month component - month = null - for component, i in components.slice() - - if month_names.hasOwnProperty(component) - month = month_names[component] - - else if month_short_names.hasOwnProperty(component) - month = month_short_names[component] - - if month isnt null - components.splice(i, 1) - break - - if month is null - return - - # Find a 4 digit year - year = null - for component, i in components.slice() - if component.length == 4 - year = Number(component) - components.splice(i, 1) - break - - # Check for 2 digit year - if year is null and components.length == 2 - year = Number(components[1]) - components.splice(1, 1) - - if year is NaN - return - - # Default to the current year - if year is null - year = (new Date()).getFullYear() - - # If the year doesn't contain the century then we add the - # current century to it. - if year < 100 - year += parseInt((new Date()).getFullYear() / 100) * 100 - - # All that's left is the day - day = Number(components[0]) - - if day is NaN - return - - # Generate the date - date = new Date(year, month, day) - - # Validate the date is as expected - if date.getFullYear() isnt year or - date.getMonth() isnt month or - date.getDate() isnt day - return - - return date - - 'iso': (s) -> - # Return a date from a string in ISO 8601 format (e.g `yyyy-mm-dd`) - - # Parse the date information - dateExp = /^(\d{4})-(\d{2})-(\d{2})$/ - match = dateExp.exec(s) - if not match - return - - # Generate a date - year = Number(match[1]) - month = Number(match[2]) - 1 - day = Number(match[3]) - - date = new Date(year, month, day) - - # Validate the date is as expected - if date.getFullYear() isnt year or - date.getMonth() isnt month or - date.getDate() isnt day - return - - return date - - 'mdy': (s) -> - # Return a date from a string using the format (`m/d/y`), the slash - # separator can be a `/`, '-', or '.' - - # Parse the date information - dateExp = /^(\d{1,2})(\/|\.|\-)(\d{1,2})(\/|\.|\-)(\d{2}|\d{4})$/ - match = dateExp.exec(s) - if not match - return - - # Generate a date - year = Number(match[5]) - month = Number(match[1]) - 1 - day = Number(match[3]) - - # If the year doesn't contain the century then we add the current - # century to it. - if year < 100 - year += parseInt((new Date()).getFullYear() / 100) * 100 - - date = new Date(year, month, day) - - # Validate the date is as expected - if date.getFullYear() isnt year or - date.getMonth() isnt month or - date.getDate() isnt day - return - - return date - - -module.exports = {Calendar: Calendar} \ No newline at end of file diff --git a/coffee/src/scripts/date-picker.coffee b/coffee/src/scripts/date-picker.coffee deleted file mode 100644 index 8cbc290..0000000 --- a/coffee/src/scripts/date-picker.coffee +++ /dev/null @@ -1,540 +0,0 @@ -$ = require 'manhattan-essentials' - -Calendar = require('./calendar.coffee').Calendar - - -class BasePicker - - # A base class that provides some shared functionality for date pickers. - - constructor: () -> - - # Flag indicating if the picker is currently open - @_isOpen = false - Object.defineProperty(this, 'isOpen', {get: () => return @_isOpen}) - - # Set up event listeners for the picker - $.listen window, - 'fullscreenchange orientationchange resize': (ev) => - # We close the date picker if the window is resized - if @isOpen - @close('resize') - - # Public methods - - close: (input, block, reason) => - # Close the picker - - # Check the picker is open (otherwise there's nothing for us to do) - if not @isOpen - return - - # Hide the picker visually - @_dom.picker.classList.add(@_bem(block, '', 'closed')) - - # Flag the picker as closed - @_isOpen = false - - # Dispatch a close event - $.dispatch(input, @_et('close'), {'reason': reason}) - - # Private methods - - _bem: (block, element='', modifier='') -> - # Build and return a class name - name = block - if element - name = "#{name}__#{element}" - if modifier - name = "#{name}--#{modifier}" - return name - - _track: (input) -> - # Position and size the picker inline with an input element - - # Get the position of the input - rect = input.getBoundingClientRect() - top = rect.top += window.scrollY - left = rect.left += window.scrollX - - # Position the picker - @_dom.picker.style.top = "#{top + rect.height}px" - @_dom.picker.style.left = "#{left}px" - - -class DatePicker extends BasePicker - - # A class that provides a date picker against a form field. - - @clsPrefix: 'data-mh-date-picker--' - - constructor: (input, options={}) -> - super() - - # Configure the instance - $.config( - this, - { - # Flag indicating if the picker should close when a date is - # picked. - closeOnPick: false, - - # The format that dates should be displayed in - format: 'human_en', - - # A list of date parsers that will be used to attempt to parse - # dates in string format. - parsers: ['human_en', 'dmy', 'iso'] - }, - options, - input, - @constructor.clsPrefix - ) - - # Set up and configure behaviours - @_behaviours = {} - - $.config( - @_behaviours, - { - input: 'set-value' - }, - options, - input, - @constructor.clsPrefix - ) - - # Domain for related DOM elements - @_dom = {} - - # Store a reference to the input the date picker is being applied to (we - # also store a reverse reference to this instance against the input). - @_dom.input = input - @_dom.input.__mh_datePicker = this - - # Build the elements required for the picker - @_dom.picker = $.create( - 'div', - { - 'class': [ - @_bem('mh-date-picker'), - @_bem('mh-date-picker', '', 'closed') - ].join(' ') - } - ) - document.body.appendChild(@_dom.picker) - - # Setup the calendar for the date picker - proxyOptions = Calendar.proxyOptions(options, input) - @_calendar = new Calendar(@_dom.picker, proxyOptions) - - # Define read-only properties - Object.defineProperty(this, 'calendar', {get: () => @_calendar}) - Object.defineProperty(this, 'input', {value: @_dom.input}) - - # Set up event listeners for the date picker - $.listen @input, - 'blur': () => - @close('blur') - 'click': () => @open() - 'focus': () => @open() - - 'change': (ev) => - # Ignore the request if the caller is the date picker itself - if ev.caller is this - return - - # When the input changes attempt to parse its value as a date - # and replace the value with a formatted date. - date = Calendar.parseDate(@input.value, @parsers) - if date - @pick(date, 'input') - - # Set up event listeners for the calendar - eventListeners = {} - eventListeners[@calendar._et('pick')] = (ev) => - @pick(ev.date, 'calendar') - - $.listen(@calendar.calendar, eventListeners) - - # Public methods - - close: (reason) -> - super(@input, 'mh-date-picker', reason) - - open: () => - # Open the date picker - - # Parse the input's value as a date and if valid select it in the - # calendar. - date = Calendar.parseDate(@input.value, @parsers) - if date - @calendar.goto(date.getMonth(), date.getFullYear()) - @calendar.select(date) - - # Track the position of the picker inline with the input - @_track() - - # Display the date picker visually - @_dom.picker.classList.remove(@_bem('mh-date-picker', '', 'closed')) - - # Flag the picker as open - @_isOpen = true - - # Dispatch an open event - $.dispatch(@input, @_et('open')) - - pick: (date, source='') -> - # Pick a date - - # Select the date in the calendar - @calendar.select(date) - - # Dispatch a pick event against the input - if $.dispatch(@input, @_et('pick'), {'date': date, 'source': source}) - - # Set the date value - @constructor.behaviours.input[@_behaviours.input](this, date) - - # Dispatch a picked event - $.dispatch(@input, @_et('picked'), {'date': date, 'source': source}) - - # Close the date picker if configured to - if @closeOnPick - @close({'reason': 'pick'}) - - # Private methods - - _et: (eventName) -> - # Generate an event type name - return "mh-date-picker--#{eventName}" - - _track: () -> - super(@input) - - # Behaviours - - @behaviours: - - # The `input` behaviour is used to set the value of the input when a - # date is selected. - input: - 'set-hidden': (datePicker, date) -> - # Set the value of the input to one formatted date and the value - # of an associated hidden field to another. - - # Set the input's value - dateStr = Calendar.formats[datePicker.format](date) - datePicker.input.value = dateStr - - # Find the associated hidden field - hiddenSelector = datePicker.input.getAttribute( - "#{datePicker.constructor.clsPrefix}hidden" - ) - hidden = $.one(hiddenSelector) - - # Find the associated hidden format - hiddenFormat = datePicker.input.getAttribute( - "#{datePicker.constructor.clsPrefix}hidden-format" - ) - - # Set the hidden fields value - hiddenDateStr = Calendar.formats[hiddenFormat](date) - hidden.value = hiddenDateStr - $.dispatch(hidden, 'change') - - 'set-value': (datePicker, date) -> - # Set the value of the input to the formatted date - - # Set the input's value - dateStr = Calendar.formats[datePicker.format](date) - datePicker.input.value = dateStr - - # We trigger a change event to help other applications detect - # the change to the input field, we send the caller argument - # with the event to prevent the event trigger a infinite cycle. - $.dispatch(datePicker.input, 'change', {caller: datePicker}) - - console.log dateStr - - -class DateRangePicker extends BasePicker - - # A class that provides a date range picker against a form field. - - @clsPrefix: 'data-mh-date-range-picker--' - - constructor: (startInput, endInput, options={}) -> - super() - - # Configure the instance - $.config( - this, - { - # Flag indicating if the picker should close when a date is - # picked. - closeOnPick: false, - - # The format that dates should be displayed in - format: 'human_en', - - # A list of date parsers that will be used to attempt to parse - # dates in string format. - parsers: ['human_en', 'dmy', 'iso'], - - # Flag indicating if the date range picker should always open - # relative to the start date (even when the end date has focus). - pinToStart: false - }, - options, - startInput, - @constructor.clsPrefix - ) - - # Set up and configure behaviours - @_behaviours = {} - - $.config( - @_behaviours, - { - input: 'set-value' - }, - options, - startInput, - @constructor.clsPrefix - ) - - # Domain for related DOM elements - @_dom = {} - - # Build the elements required for the picker - @_dom.picker = $.create( - 'div', - { - 'class': [ - @_bem('mh-date-range-picker'), - @_bem('mh-date-range-picker', '', 'closed') - ].join(' ') - } - ) - document.body.appendChild(@_dom.picker) - - # Store a reference to the inputs the date range picker is being applied - # to (we also store a reverse reference to this instance against the - # inputs). - @_dom.startInput = startInput - @_dom.startInput.__mh_dateRangePicker = this - @_dom.endInput = endInput - @_dom.endInput.__mh_dateRangePicker = this - - # Setup the calendars for the date range picker - proxyOptions = Calendar.proxyOptions(options, startInput) - @_calendars = [ - new Calendar(@_dom.picker, proxyOptions) - new Calendar(@_dom.picker, proxyOptions) - ] - - # Flag indicating which date (start or end) we're picking in the range - @_picking = 'start' - - # Define read-only properties - Object.defineProperty(this, 'calendars', {get: () => @_calendars}) - Object.defineProperty(this, 'endInput', {value: @_dom.endInput}) - Object.defineProperty(this, 'startInput', {value: @_dom.startInput}) - Object.defineProperty(this, 'picking', {get: () => return @_picking}) - - # Set up event listeners for the date picker - for input in [@startInput, @endInput] - $.listen input, - 'click': (ev) -> - ev.target.focus() - - 'focus': (ev) => - # Flag the date that's being picked - if ev.target is @startInput - @_picking = 'start' - else - @_picking = 'end' - @open() - - 'blur': () => - # Check if the active element is one of the picker's inputs - # (e.g when switching between date inputs), if so then we - # don't want to close the picker. - activeInput = document.activeElement - if @startInput is activeInput or @endInput is activeInput - return - - @close('blur') - - 'change': (ev) => - # When the input changes attempt to parse its value as a - # date and replace the value with a fixed format date. - - # Ignore the request if the caller is the date picker itself - if ev.caller is this - return - - # Parse the date - date = Calendar.parseDate(ev.target.value, @parsers) - if date - - # Build the date range - dateRange = @calendars[0].dateRange - if @picking is 'start' - dateRange[0] = date - else - dateRange[1] = date - - @pick(dateRange, {'source': 'input'}) - - # Set up event listeners for the calendar - eventListeners = {} - - eventListeners[@calendars[0]._et('pick')] = (ev) => - - # Build the date range - dateRange = @calendars[0].dateRange - if @picking is 'start' - dateRange[0] = ev.date - else - dateRange[1] = ev.date - - @pick(dateRange, {'source': 'calendar'}) - - eventListeners[@calendars[0]._et('view')] = (ev) => - # Make sure the adjacent calendar is one month on - if @calendars.indexOf(ev.calendar) is 0 - @calendars[1].sync(@calendars[0], 1) - else - @calendars[0].sync(@calendars[1], -1) - - $.listen(@calendars[0].calendar, eventListeners) - $.listen(@calendars[1].calendar, eventListeners) - - # Public methods - - close: (reason) -> - super(@startInput, 'mh-date-range-picker', reason) - - open: () -> - # Open the date range picker - - # Determine which input the picker is being open against - input = @startInput - if @picking == 'end' - input = @endInput - - # Parse the date inputs and build a date range to select in the - # calendars. - startDate = Calendar.parseDate(@startInput.value, @parsers) - endDate = Calendar.parseDate(@endInput.value, @parsers) - dateRange = @calendars[0].dateRange - if startDate - dateRange[0] = startDate - if endDate - dateRange[1] = endDate - - # If the start date is after the end date then set the value of the - # start input and focus on the end input. - if dateRange[1] < dateRange[0] - @pick([dateRange[1], dateRange[0]]) - @endInput.focus() - return - - # Select the current date range - for calendar in @calendars - calendar.select(dateRange[0], dateRange[1]) - - # If neither calendar contains the start or end date in the range then - # set the first calendar to show the start date. - startStr = "#{dateRange[0].getMonth()}.#{dateRange[0].getFullYear()}" - endStr = "#{dateRange[1].getMonth()}.#{dateRange[1].getFullYear()}" - viewStrs = [ - "#{@calendars[0].month}.#{@calendars[0].year}", - "#{@calendars[1].month}.#{@calendars[1].year}" - ] - if viewStrs.indexOf(startStr) is -1 and viewStrs.indexOf(endStr) is -1 - viewDate = dateRange[0] - @calendars[0].goto(viewDate.getMonth(), viewDate.getFullYear()) - - # Track the position of the picker inline with the input - @_track(if @pinToStart then @startInput else input) - - # Display the date picker visually - closedClass = @_bem('mh-date-range-picker', '', 'closed') - @_dom.picker.classList.remove(closedClass) - - # Flag the picker as open - @_isOpen = true - - # Dispatch an open event - $.dispatch(@startInput, @_et('open')) - - pick: (dateRange, source) -> - # Pick a date - - # Set the date range for both calendar - for calendar in @calendars - calendar.select(dateRange[0], dateRange[1]) - - # Dispatch a pick event against the input - evData = {'dateRange': dateRange, 'source': source} - if $.dispatch(@startInput, @_et('pick'), evData) - - # Set the date value - @constructor.behaviours.input[@_behaviours.input](this, dateRange) - - # Dispatch a picked event - $.dispatch(@startInput, @_et('picked'), evData) - - # Switch the focus to the next date input - if @picking is 'start' - @endInput.focus() - else - @startInput.focus() - - # Close the date picker if configured to - if @closeOnPick - @close() - - # Private methods - - _et: (eventName) -> - # Generate an event type name - return "mh-date-range-picker--#{eventName}" - - # Behaviours - - @behaviours: - - # The `input` behaviour is used to set the value of the input when a - # date is selected. - input: - 'set-value': (dateRangePicker, dateRange) -> - # Set the input values - format = Calendar.formats[dateRangePicker.format] - dateRangePicker.startInput.value = format(dateRange[0]) - $.dispatch( - dateRangePicker.startInput, - 'change', - {caller: datePicker} - ) - dateRangePicker.endInput.value = format(dateRange[1]) - $.dispatch( - dateRangePicker.endInput, - 'change', - {caller: datePicker} - ) - - -module.exports = { - - # The calendar is exported to allow new behaviours, date formats and parsers - # to be defined. - Calendar: Calendar, - - # UI component classes - DatePicker: DatePicker, - DateRangePicker: DateRangePicker - } \ No newline at end of file diff --git a/coffee/src/styles/date-picker.scss b/coffee/src/styles/date-picker.scss deleted file mode 100644 index f13d9c4..0000000 --- a/coffee/src/styles/date-picker.scss +++ /dev/null @@ -1,206 +0,0 @@ -$date-size: 280 / 7; -$half-date-size: $date-size / 2; - -.mh-calendar { - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - - box-sizing: border-box; - float: left; - font-family: sans-serif; - padding: 10px; - user-select: none; - width: 300px; - - * { - box-sizing: border-box; - } - - // Nav - &__nav { - position: relative; - } - - &__month { - color: #666; - font-size: 18px; - text-align: center; - } - - &__next { - border-bottom: 8px solid transparent; - border-left: 12px solid #334144; - border-radius: 3px; - border-top: 8px solid transparent; - cursor: pointer; - height: 0; - position: absolute; - right: 10px; - top: 2px; - width: 0; - } - - &__previous { - border-bottom: 8px solid transparent; - border-radius: 3px; - border-right: 12px solid #334144; - border-top: 8px solid transparent; - cursor: pointer; - height: 0; - left: 10px; - position: absolute; - top: 2px; - width: 0; - } - - // Weekdays - &__weekdays { - margin-top: 10px; - - &::after { - clear: both; - content: ' '; - display: table; - } - } - - &__weekday { - color: #999; - float: left; - font-size: 11px; - font-weight: bold; - text-align: center; - width: #{$date-size}px; - } - - // Dates - &__dates { - margin-top: 10px; - - &::after { - clear: both; - content: ' '; - display: table; - } - } - - &__date { - color: #666; - float: left; - font-size: 14px; - height: #{$date-size}px; - line-height: #{$date-size}px; - position: relative; - text-align: center; - width: #{$date-size}px; - - &:hover { - cursor: pointer; - - &::after { - background: rgba(226, 106, 106, 0.1); - border-radius: #{$half-date-size}px; - content: ''; - display: block; - height: #{$date-size}px; - left: 0; - position: absolute; - top: 0; - width: #{$date-size}px; - z-index: -1; - } - } - - &--in-range { - background: rgba(226, 106, 106, 0.1); - } - - &--range-start { - background: #e26a6a; - border-bottom-left-radius: #{$half-date-size}px; - border-top-left-radius: #{$half-date-size}px; - color: #fff; - font-weight: bold; - - &:hover { - cursor: default; - - &::after { - display: none; - } - } - } - - &--range-end { - background: #e26a6a; - border-bottom-right-radius: #{$half-date-size}px; - border-top-right-radius: #{$half-date-size}px; - color: #fff; - font-weight: bold; - - &:hover { - cursor: default; - - &::after { - display: none; - } - } - } - - &--today { - text-decoration: underline; - } - - &--blocked { - color: #999; - font-style: italic; - font-weight: normal; - - &:hover { - cursor: default; - - &::after { - display: none; - } - } - } - - &--range-start.mh-calendar__date--blocked, - &--range-end.mh-calendar__date--blocked { - color: #fff; - } - } -} - -.mh-date-picker, -.mh-date-range-picker { - background: #fff; - border: 1px solid #d4dce0; - border-radius: 3px; - margin-top: 10px; - position: absolute; - width: 300px; - z-index: 4; - - &--closed { - display: none; - } -} - -.mh-date-range-picker { - width: 600px; - - .mh-calendar:first-child { - .mh-calendar__next { - display: none; - } - } - - .mh-calendar:nth-child(2) { - .mh-calendar__previous { - display: none; - } - } -} \ No newline at end of file diff --git a/coffee/test/calendar.coffee b/coffee/test/calendar.coffee deleted file mode 100644 index acdbe9c..0000000 --- a/coffee/test/calendar.coffee +++ /dev/null @@ -1,555 +0,0 @@ -# Imports - -chai = require 'chai' -jsdom = require 'mocha-jsdom' -sinon = require 'sinon' -sinonChai = require 'sinon-chai' - -$ = require 'manhattan-essentials' -Calendar = require('../src/scripts/calendar').Calendar - - -# Set up - -should = chai.should() -chai.use(sinonChai) - - -# Tests - -describe 'Calendar (class)', -> - - jsdom() - - calendar = null - - beforeEach -> - # Initialize a calendar - calendar = new Calendar(document.body) - calendar.goto(0, 2000) - - afterEach -> - calendar.destroy() - - describe 'constructor', -> - - it 'should generate a new `Calendar` instance', -> - - calendar.should.be.an.instanceof Calendar - - it 'should add the calendar element to the body', -> - - $.one('.mh-calendar', document.body).should.exist - - describe 'destroy', -> - - it 'should remove the calendar element from the body', -> - - calendar.destroy() - should.not.exist($.one('.mh-calendar', document.body)) - - describe 'goto', -> - - it 'should set the month and year for the calendar to those given', -> - calendar.goto(5, 1979) - calendar.month.should.equal 5 - calendar.year.should.equal 1979 - - it 'should dispatch a view event against the calendar DOM element', -> - - listener = sinon.spy() - calendar.calendar.addEventListener('mh-calendar--view', listener) - calendar.goto(5, 1979) - listener.should.have.been.calledOn calendar.calendar - - ev = listener.args[0][0] - ev.calendar.should.equal calendar - ev.month.should.equal 5 - ev.year.should.equal 1979 - - describe 'next', -> - - it 'should advance the month and year the calendar displays by one - month', -> - calendar.next() - calendar.month.should.equal 1 - calendar.year.should.equal 2000 - - describe 'offset', -> - - it 'should move the month and year the calendar displays by the given - number of months and years', -> - calendar.offset(-7, -20) - calendar.month.should.equal 5 - calendar.year.should.equal 1979 - - describe 'previous', -> - - it 'should move the month and year the calendar displays back by one - month', -> - calendar.previous() - calendar.month.should.equal 11 - calendar.year.should.equal 1999 - - describe 'select', -> - - it 'should select the given date', -> - date = new Date(1979, 5, 11) - calendar.select(date) - calendar.dateRange[0].getTime().should.equal date.getTime() - calendar.dateRange[1].getTime().should.equal date.getTime() - - it 'should select the given date range', -> - dateRange = [new Date(1979, 5, 11), new Date(1978, 6, 25)] - calendar.select(dateRange[0], dateRange[1]) - calendar.dateRange[0].getTime().should.equal dateRange[0].getTime() - calendar.dateRange[1].getTime().should.equal dateRange[1].getTime() - - describe 'sync', -> - - otherCalendar = null - - before -> - otherCalendar = new Calendar(document.body) - otherCalendar.goto(11, 2001) - - after -> - otherCalendar.destroy() - - it "it should sync the calendar view with another calendar's view at the - given offset", -> - - calendar.sync(otherCalendar, 1) - calendar.month.should.equal 0 - calendar.year.should.equal 2002 - - describe 'update', -> - - before -> - calendar.test = 'excluding' - calendar.dates = [ - new Date(2000, 1, 1), - new Date(2000, 1, 2), - new Date(2000, 1, 3) - ] - - after -> - calendar.test = 'any' - calendar.dates = [] - - it 'should update the view to display the given month and year', -> - calendar.goto(5, 1979) - - dom = calendar._dom - dom.month.textContent.should.equal 'June, 1979' - - # Check the first and last day of the month are displayed in the - # correct slots. - dom.dates.childNodes[4].textContent == '1' - dom.dates.childNodes[(4 * 7) + 5].textContent == '1' - - it 'should flag dates in the current date range', -> - startDate = new Date(2000, 0, 2) - endDate = new Date(2000, 0, 11) - calendar.goto(0, 2000) - calendar.select(startDate, endDate) - - startTime = startDate.getTime() - endTime = endDate.getTime() - for dateElm in calendar._dom.dates.childNodes - thisTime = dateElm.__mh_date.getTime() - - if thisTime is startTime - dateElm.classList.contains( - 'mh-calendar__date--range-start').should.be.true - - if thisTime is endTime - dateElm.classList.contains( - 'mh-calendar__date--range-end').should.be.true - - if thisTime > startTime and thisTime < endTime - dateElm.classList.contains( - 'mh-calendar__date--in-range').should.be.true - - it 'should flag dates outside of the month as blocked', -> - calendar.goto(0, 2000) - - for dateElm in calendar._dom.dates.childNodes - if dateElm.__mh_date.getMonth() isnt 0 - dateElm.classList.contains( - 'mh-calendar__date--blocked').should.be.true - - it 'should flag the current date as today', -> - today = new Date() - calendar.goto(today.getMonth(), new Date().getFullYear()) - - for dateElm in calendar._dom.dates.childNodes - if dateElm.__mh_date.getTime() is today.getTime() - dateElm.classList.contains( - 'mh-calendar__date--today').should.be.true - - it "should flag dates that don't pass the test behaviour as blocked", -> - calendar.goto(0, 2000) - excludedTimes = (d.getTime() for d in calendar.dates) - - for dateElm in calendar._dom.dates.childNodes - if excludedTimes.indexOf(dateElm.__mh_date.getTime()) > -1 - dateElm.classList.contains( - 'mh-calendar__date--blocked').should.be.true - - describe '@parseDate', -> - - it 'should attempt to parse a date string using the list of named - parsers', -> - - # Spy on the parsers we'll be calling - human_en = sinon.spy(Calendar.parsers, 'human_en') - dmy = sinon.spy(Calendar.parsers, 'dmy') - iso = sinon.spy(Calendar.parsers, 'iso') - - date = Calendar.parseDate('1979-06-11', ['human_en', 'dmy', 'iso']) - - human_en.should.have.been.called - dmy.should.have.been.called - iso.should.have.been.called - - date.getTime().should.equal new Date(1979, 5, 11).getTime() - - describe '@proxyOptions', -> - - input = null - - before -> - - input = $.create('input', { - 'data-mh-calendar--dates': '2000-01-02,Jan 3rd 2000,4.1.00', - 'data-mh-calendar--min-date': '1990-01-01', - 'data-mh-calendar--max-date': '2nd February 2010', - 'data-mh-calendar--first-weekday': '0', - 'data-mh-calendar--month-names': \ - 'Ja,Fe,Ma,Ap,Ma,Ju,Ju,Au,Se,Oc,No,De', - 'data-mh-calendar--weekday-names': 'Su,Mo,Tu,We,Th,Fr,Sa' - }) - - it 'Return a set of config options for the calendar based on a set of - user defined options and an input element', -> - - # Defaults (no options, no input) - proxy = Calendar.proxyOptions( - 'data-mh-calendar--', - {}, - null - ) - proxy.should.deep.equal Calendar.getDefaultConfig() - - # Options (no input) - options = { - dates: [new Date(2000, 2, 10), new Date(2000, 2, 11)], - minDate: new Date(2000, 1, 10), - maxDate: new Date(2000, 3, 10), - firstWeekday: 2, - monthNames: 'J,F,M,A,M,J,J,A,S,O,N,D'.split(','), - weekdayNames: 'S,M,T,W,T,F,S'.split(','), - parsers: ['human_en', 'dmy', 'iso'] - } - proxy = Calendar.proxyOptions( - 'data-mh-calendar--', - options, - null - ) - proxy.should.deep.equal { - dates: [new Date(2000, 2, 10), new Date(2000, 2, 11)], - minDate: new Date(2000, 1, 10), - maxDate: new Date(2000, 3, 10), - firstWeekday: 2, - monthNames: 'J,F,M,A,M,J,J,A,S,O,N,D'.split(','), - weekdayNames: 'S,M,T,W,T,F,S'.split(',') - } - - # Options and input - proxy = Calendar.proxyOptions( - 'data-mh-calendar--', - options, - input - ) - proxy.should.deep.equal { - dates: [ - new Date(2000, 0, 2), - new Date(2000, 0, 3), - new Date(2000, 0, 4) - ], - minDate: new Date(1990, 0, 1), - maxDate: new Date(2010, 1, 2), - firstWeekday: 0, - monthNames: 'Ja,Fe,Ma,Ap,Ma,Ju,Ju,Au,Se,Oc,No,De'.split(','), - weekdayNames: 'Su,Mo,Tu,We,Th,Fr,Sa'.split(',') - } - - -describe 'Calendar (options)', -> - - jsdom() - - describe 'dates', -> - - # `dates` isn't tested as an option as it is covered under tests for the - # `test` behaviour. - - describe 'minDate', -> - - it 'should set all dates before the minimum date as blocked', -> - - minDate = new Date(10, 0, 2000) - calendar = new Calendar(document.body, {minDate: minDate}) - calendar.goto(0, 2000) - - for dateElm in calendar._dom.dates.childNodes - if dateElm.__mh_date.getTime() < minDate.getTime() - dateElm.classList.contains( - 'mh-calendar__date--blocked').should.be.true - - describe 'maxDate', -> - - it 'should set all dates after the maximum date as blocked', -> - - maxDate = new Date(20, 0, 2000) - calendar = new Calendar(document.body, {maxDate: maxDate}) - calendar.goto(0, 2000) - - for dateElm in calendar._dom.dates.childNodes - if dateElm.__mh_date.getTime() > maxDate.getTime() - dateElm.classList.contains( - 'mh-calendar__date--blocked').should.be.true - - describe 'firstWeekday', -> - - it 'should set first weekday displayed in the calendar view', -> - - maxDate = new Date(20, 0, 2000) - calendar = new Calendar(document.body, {firstWeekday: 0}) - calendar.goto(0, 2000) - - # Check the order of the weekdays - weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] - for weekdayElm, i in calendar._dom.weekdays - weekdayElm.textContent.should.equal weekdays[i] - - # Check that every 7th date is a Sunday - for weekdayElm, i in calendar._dom.weekdays - if i % 7 is 0 - weekdayElm.__mh_date.getDay().should.equal 0 - - describe 'monthNames', -> - - it 'should set the month names displayed in the calendar view', -> - - monthNames = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec' - monthNames = monthNames.split(',') - calendar = new Calendar(document.body, {monthNames: monthNames}) - - monthElm = calendar._dom.month - for name, i in monthNames - calendar.goto(i, 2000) - monthElm.textContent.should.equal "#{name}, 2000" - - describe 'weekdayNames', -> - - it 'should set the weekday names displayed in the calendar view', -> - - weekdayNames = 'Su,Mo,Tu,We,Th,Fr,Sa'.split(',') - calendar = new Calendar( - document.body, - { - firstWeekday: 0, - weekdayNames: weekdayNames - } - ) - - weekdaysElm = calendar._dom.weekdays - for name, i in weekdayNames - calendar.goto(i, 2000) - weekdayElm = weekdaysElm.childNodes[i] - weekdayElm.textContent.should.equal name - - -describe 'Calendar (behaviours)', -> - - jsdom() - - calendar = null - - beforeEach -> - # Initialize a calendar - calendar = new Calendar(document.body) - calendar.goto(0, 2000) - - describe 'test', -> - - describe 'any', -> - - it 'should return true for any date', -> - - behaviour = Calendar.behaviours.test.any - date = new Date(2000, 0, 1) - for i in [0..365] - behaviour(calendar, [], date).should.be.true - date.setDate(date.getDate() + 1) - - - describe 'excluding', -> - - it 'should return true only for dates not in the `dates` config - option', -> - - behaviour = Calendar.behaviours.test.excluding - date = new Date(2000, 0, 1) - dates = [ - new Date(2000, 1, 1), - new Date(2000, 2, 2), - new Date(2000, 3, 3) - ] - times = (d.getTime() for d in dates) - - for i in [0..365] - if times.indexOf(date.getTime()) > -1 - behaviour(calendar, dates, date).should.be.false - else - behaviour(calendar, dates, date).should.be.true - date.setDate(date.getDate() + 1) - - describe 'only', -> - - it 'should return true only for dates in the `dates` config - option', -> - - behaviour = Calendar.behaviours.test.only - date = new Date(2000, 0, 1) - dates = [ - new Date(2000, 1, 1), - new Date(2000, 2, 2), - new Date(2000, 3, 3) - ] - times = (d.getTime() for d in dates) - - for i in [0..365] - if times.indexOf(date.getTime()) > -1 - behaviour(calendar, dates, date).should.be.true - else - behaviour(calendar, dates, date).should.be.false - date.setDate(date.getDate() + 1) - - describe 'weekdays', -> - - it 'should return true only for dates in that fall on the weekdays - in the `dates` config option', -> - - behaviour = Calendar.behaviours.test.weekdays - date = new Date(2000, 0, 1) - weekdays = [0, 6] - - for i in [0..365] - if weekdays.indexOf(date.getDay()) > -1 - behaviour(calendar, weekdays, date).should.be.true - else - behaviour(calendar, weekdays, date).should.be.false - date.setDate(date.getDate() + 1) - - -describe 'Calendar (formats)', -> - - describe 'dmy', -> - - it 'should format a date as `dd/mm/yyyy`', -> - - Calendar.formats.dmy(new Date(2000, 0, 2)).should.equal '02/01/2000' - - describe 'human_en', -> - - it 'should format a date as `{day} {full month name} {full year}`, e.g - `22 January 2011`', -> - - s = Calendar.formats.human_en(new Date(2000, 0, 2)) - s.should.equal '2 January 2000' - - describe 'human_abbr_en', -> - - it 'should format a date as - `{day} {abbreviated month name} {full year}`, e.g `22 Jan 2011`', -> - - s = Calendar.formats.human_abbr_en(new Date(2000, 0, 2)) - s.should.equal '2 Jan 2000' - - describe 'dmy', -> - - it 'should format a date as ISO 8601 (e.g `yyyy-mm-dd`)', -> - - s = Calendar.formats.iso(new Date(2000, 0, 2)) - s.should.equal '2000-01-02' - - describe 'mdy', -> - - it 'should format a date as `mm/dd/yyyy`', -> - - Calendar.formats.mdy(new Date(2000, 0, 2)).should.equal '01/02/2000' - - -describe 'Calendar (parsers)', -> - - month = new Date().getMonth() - time = new Date(2000, 0, 2).getTime() - year = new Date().getFullYear() - - describe 'dmy', -> - - it 'should parse a date string using the format `d/m/y`', -> - Calendar.parsers.dmy('2/1/00').getTime().should.equal time - Calendar.parsers.dmy('2/1/2000').getTime().should.equal time - Calendar.parsers.dmy('02/01/2000').getTime().should.equal time - - it 'should parse a date string using the format `d.m.y`', -> - Calendar.parsers.dmy('2.01.00').getTime().should.equal time - - it 'should parse a date string using the format `d-m-y`', -> - Calendar.parsers.dmy('2-1-00').getTime().should.equal time - - describe 'human_en', -> - - # A series of human readable dates formats that must be parsed - - it 'should parse `2nd`', -> - date = Calendar.parsers.human_en('2nd') - date.getTime().should.equal (new Date(year, month, 2)).getTime() - - it 'should parse `1 Aug`', -> - date = Calendar.parsers.human_en('1 Aug') - date.getTime().should.equal (new Date(year, 7, 1)).getTime() - - it 'should parse `15 Feb 17`', -> - date = Calendar.parsers.human_en('15 Feb 17') - date.getTime().should.equal (new Date(2017, 1, 15)).getTime() - - it 'should parse `January 22, 2011`', -> - date = Calendar.parsers.human_en('January 22, 2011') - date.getTime().should.equal (new Date(2011, 0, 22)).getTime() - - it 'should parse `Nov 22`', -> - date = Calendar.parsers.human_en('Nov 22') - date.getTime().should.equal (new Date(year, 10, 22)).getTime() - - describe 'iso', -> - - it 'should parse a date string using the format `yyyy-mm-dd`', -> - Calendar.parsers.iso('2000-01-02').getTime().should.equal time - - describe 'mdy', -> - - it 'should parse a date string using the format `m/d/y`', -> - Calendar.parsers.mdy('1/2/00').getTime().should.equal time - Calendar.parsers.mdy('1/2/2000').getTime().should.equal time - Calendar.parsers.mdy('01/02/2000').getTime().should.equal time - - it 'should parse a date string using the format `m.d.y`', -> - Calendar.parsers.mdy('1.2.00').getTime().should.equal time - - it 'should parse a date string using the format `m-d-y`', -> - Calendar.parsers.mdy('1-2-00').getTime().should.equal time diff --git a/coffee/test/date-picker.coffee b/coffee/test/date-picker.coffee deleted file mode 100644 index 8a0b8dd..0000000 --- a/coffee/test/date-picker.coffee +++ /dev/null @@ -1,311 +0,0 @@ -# Imports - -chai = require 'chai' -jsdom = require 'mocha-jsdom' -sinon = require 'sinon' -sinonChai = require 'sinon-chai' - -$ = require 'manhattan-essentials' -DatePicker = require('../src/scripts/date-picker').DatePicker - - -# Set up - -should = chai.should() -chai.use(sinonChai) - - -# Tests - -describe 'DatePicker (class)', -> - - jsdom() - - form = null - input = null - datePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - input = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-picker': true - } - ) - input.getBoundingClientRect = -> - return { - bottom: 20, - height: 10, - left: 10, - right: 110, - top: 10, - width: 100 - } - - document.body.appendChild(form) - form.appendChild(input) - - # Initialize a date picker for the input field - datePicker = new DatePicker(input) - - describe 'constructor', -> - - it 'should generate a new `DatePicker` instance', -> - - datePicker.should.be.an.instanceof DatePicker - - it 'should add the date picker element to the body', -> - - $.one('.mh-date-picker', document.body).should.exist - - describe 'close', -> - - beforeEach -> - datePicker.open() - - it 'should flag the date picker as closed', -> - - datePicker.close() - datePicker.isOpen.should.be.false - - it 'should add a closed CSS class to the date picker', -> - - datePicker.close() - pickerElm = datePicker._dom.picker - closed = 'mh-date-picker--closed' - pickerElm.classList.contains(closed).should.be.true - - it 'should dispatch a close event against the input', -> - - listener = sinon.spy() - input.addEventListener('mh-date-picker--close', listener) - datePicker.close('test') - listener.should.have.been.calledOn input - - ev = listener.args[0][0] - ev.reason.should.equal 'test' - - describe 'open', -> - - beforeEach -> - datePicker.close() - - it 'should flag the date picker as open', -> - - datePicker.open() - datePicker.isOpen.should.be.true - - it 'should remove the closed CSS class from the date picker', -> - - datePicker.open() - pickerElm = datePicker._dom.picker - closed = 'mh-date-picker--closed' - pickerElm.classList.contains(closed).should.be.false - - it 'should position the date picker in-line with the input', -> - - datePicker.open() - - pickerElm = datePicker._dom.picker - pickerElm.style.left.should.equal '10px' - pickerElm.style.top.should.equal '20px' - - it 'should dispatch an open event against the input', -> - - listener = sinon.spy() - input.addEventListener('mh-date-picker--open', listener) - datePicker.open() - listener.should.have.been.calledOn input - - it 'should show a calendar view for the date value in the input', -> - - datePicker.open() - datePicker.calendar.month.should.equal 0 - datePicker.calendar.year.should.equal 2000 - - describe 'pick', -> - - date = new Date(2001, 2, 3) - - beforeEach -> - input.value = '1 Jan 2000' - datePicker.calendar.select(new Date(2000, 0, 1)) - - it 'should select the date in the calendar', -> - - datePicker.pick(date) - dateRange = datePicker.calendar.dateRange - dateRange[0].getTime().should.deep.equal date.getTime() - dateRange[1].getTime().should.deep.equal date.getTime() - - it 'should set the value of the input to the picked date', -> - - datePicker.pick(date) - input.value.should.equal '3 March 2001' - - it 'should dispatch a pick and picked event against the input', -> - - pickListener = sinon.spy() - pickedListener = sinon.spy() - input.addEventListener('mh-date-picker--pick', pickListener) - input.addEventListener('mh-date-picker--picked', pickedListener) - - datePicker.pick(date, 'test') - - pickEv = pickListener.args[0][0] - pickedEv = pickedListener.args[0][0] - - pickListener.should.have.been.calledOn input - pickEv.date.should.equal date - pickEv.source.should.equal 'test' - - pickedListener.should.have.been.calledOn input - pickedEv.date.should.equal date - pickedEv.source.should.equal 'test' - - -describe 'DatePicker (options)', -> - - jsdom() - - form = null - input = null - datePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - input = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-picker': true - } - ) - document.body.appendChild(form) - form.appendChild(input) - - afterEach -> - document.body.removeChild(datePicker._dom.picker) - - describe 'closeOnPick', -> - - describe 'when false', -> - - before -> - datePicker = new DatePicker(input, {closeOnPick: false}) - - it 'should keep the date picker open once a date has been - picked', -> - - datePicker.open() - datePicker.pick(new Date(2000, 0, 2)) - datePicker.isOpen.should.be.true - - describe 'when true', -> - - before -> - datePicker = new DatePicker(input, {closeOnPick: true}) - - it 'should close the date picker once a date has been picked', -> - - datePicker.open() - datePicker.pick(new Date(2000, 0, 2)) - datePicker.isOpen.should.be.false - - describe 'format', -> - - describe 'when iso', -> - - before -> - datePicker = new DatePicker(input, {format: 'iso'}) - - it 'should set the input value as an ISO 8601 format date when a - date is picked', -> - - datePicker.pick(new Date(2000, 0, 2)) - input.value.should.equal '2000-01-02' - - describe 'parsers', -> - - describe 'when ["mdy"]', -> - - before -> - datePicker = new DatePicker(input, { - format: 'mdy', - parsers: ['mdy'] - }) - - it 'should only parse dates in m/d/y format when the input value is - changed', -> - - # Date should not be parsed and therefore the selected date and - # input value should not have changed. - input.value = '2000-01-02' - $.dispatch(input, 'changed') - - input.value.should.equal '2000-01-02' - - # Date should be parsed and therefore the selected date and - # input should have changed. - input.value = '02/01/2000' - $.dispatch(input, 'changed') - - input.value.should.equal '02/01/2000' - - -describe 'DatePicker (behaviours)', -> - - jsdom() - - form = null - hidden = null - input = null - datePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - hidden = $.create('input', {'class': 'foo-hidden'}) - input = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-picker': true, - 'data-mh-date-picker--hidden': '.foo-hidden', - 'data-mh-date-picker--hidden-format': 'iso' - } - ) - document.body.appendChild(form) - form.appendChild(hidden) - form.appendChild(input) - datePicker = new DatePicker(input) - - describe 'input', -> - - date = new Date(2000, 0, 1) - - describe 'set-hidden', -> - - before -> - hidden.value = '' - - it 'should set the value of an associated hidden input', -> - - behaviour = DatePicker.behaviours.input['set-hidden'] - behaviour(datePicker, date) - hidden.value.should.equal '2000-01-01' - - describe 'set-value', -> - - before -> - input.value = '' - - it 'should set the value of the input field', -> - - behaviour = DatePicker.behaviours.input['set-value'] - behaviour(datePicker, date) - input.value.should.equal '1 January 2000' \ No newline at end of file diff --git a/coffee/test/date-range-picker.coffee b/coffee/test/date-range-picker.coffee deleted file mode 100644 index 75ee6e8..0000000 --- a/coffee/test/date-range-picker.coffee +++ /dev/null @@ -1,437 +0,0 @@ -# Imports - -chai = require 'chai' -jsdom = require 'mocha-jsdom' -sinon = require 'sinon' -sinonChai = require 'sinon-chai' - -$ = require 'manhattan-essentials' -DateRangePicker = require('../src/scripts/date-picker').DateRangePicker - - -# Set up - -should = chai.should() -chai.use(sinonChai) - - -# Tests - -describe 'DateRangePicker (class)', -> - - jsdom() - - form = null - startInput = null - endInput = null - dateRangePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - startInput = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-range-picker': true - } - ) - startInput.getBoundingClientRect = -> - return { - bottom: 20, - height: 10, - left: 10, - right: 110, - top: 10, - width: 100 - } - endInput = $.create('input', {'value': '5 Jan 2000'}) - endInput.getBoundingClientRect = -> - return { - bottom: 20, - height: 10, - left: 50, - right: 150, - top: 10, - width: 100 - } - document.body.appendChild(form) - form.appendChild(startInput) - form.appendChild(endInput) - - # Initialize a date range picker for the input fields - dateRangePicker = new DateRangePicker(startInput, endInput) - - describe 'constructor', -> - - it 'should generate a new `DateRangePicker` instance', -> - - dateRangePicker.should.be.an.instanceof DateRangePicker - - it 'should add the date range picker element to the body', -> - - $.one('.mh-date-range-picker', document.body).should.exist - - describe 'close', -> - - beforeEach -> - dateRangePicker.open() - - it 'should flag the date range picker as closed', -> - - dateRangePicker.close() - dateRangePicker.isOpen.should.be.false - - it 'should add a closed CSS class to the date range picker', -> - - dateRangePicker.close() - pickerElm = dateRangePicker._dom.picker - closed = 'mh-date-range-picker--closed' - pickerElm.classList.contains(closed).should.be.true - - it 'should dispatch a close event against the start input', -> - - listener = sinon.spy() - startInput.addEventListener('mh-date-range-picker--close', listener) - dateRangePicker.close('test') - listener.should.have.been.calledOn startInput - - ev = listener.args[0][0] - ev.reason.should.equal 'test' - - describe 'open', -> - - beforeEach -> - dateRangePicker.close() - - it 'should flag the date range picker as open', -> - - dateRangePicker.open() - dateRangePicker.isOpen.should.be.true - - it 'should remove the closed CSS class from the date range picker', -> - - dateRangePicker.open() - pickerElm = dateRangePicker._dom.picker - closed = 'mh-date-range-picker--closed' - pickerElm.classList.contains(closed).should.be.false - - it 'should dispatch an open event against the start input', -> - - listener = sinon.spy() - startInput.addEventListener('mh-date-range-picker--open', listener) - dateRangePicker.open() - listener.should.have.been.calledOn startInput - - describe 'when the start date is greater than the end date', -> - - it 'should set the start input to the end input (and visa-versa) and - focus the end input', -> - - startInput.value = '10 Jan 2000' - startInput.focus() - - startInput.value.should.equal '5 January 2000' - endInput.value.should.equal '10 January 2000' - document.activeElement.should.equal endInput - - describe 'when the start input is focused', -> - - it 'should position the date range picker in-line with the start - input', -> - - startInput.focus() - - pickerElm = dateRangePicker._dom.picker - pickerElm.style.left.should.equal '10px' - pickerElm.style.top.should.equal '20px' - - describe 'when the end input is focused', -> - - it 'should position the date range picker in-line with the end - input', -> - - endInput.focus() - - pickerElm = dateRangePicker._dom.picker - pickerElm.style.left.should.equal '50px' - pickerElm.style.top.should.equal '20px' - - describe 'pick', -> - - dateRange = [new Date(2001, 1, 5), new Date(2001, 1, 10)] - - beforeEach -> - startInput.value = '1 Jan 2000' - endInput.value = '5 Jan 2000' - for calendar in dateRangePicker.calendars - calendar.select(new Date(2000, 0, 1), new Date(2000, 0, 5)) - - it 'should select the date range in the calendar', -> - - dateRangePicker.pick(dateRange) - for calendar in dateRangePicker.calendars - dates = calendar.dateRange - dates[0].getTime().should.deep.equal dateRange[0].getTime() - dates[1].getTime().should.deep.equal dateRange[1].getTime() - - it 'should set the value of the start and end inputs to the picked - date range', -> - - dateRangePicker.pick(dateRange) - startInput.value.should.equal = '5 February 2001' - endInput.value.should.equal '10 February 2001' - - it 'should dispatch a pick and picked event against the start input', -> - - pickListener = sinon.spy() - pickedListener = sinon.spy() - startInput.addEventListener( - 'mh-date-range-picker--pick', - pickListener - ) - startInput.addEventListener( - 'mh-date-range-picker--picked', - pickedListener - ) - - dateRangePicker.pick(dateRange, 'test') - - pickEv = pickListener.args[0][0] - pickedEv = pickedListener.args[0][0] - - pickListener.should.have.been.calledOn startInput - pickEv.dateRange.should.equal dateRange - pickEv.source.should.equal 'test' - - pickedListener.should.have.been.calledOn startInput - pickedEv.dateRange.should.equal dateRange - pickedEv.source.should.equal 'test' - - describe 'when the start input is focused', -> - - it 'should set the focus to the end input', -> - - startInput.focus() - dateRangePicker.pick(dateRange) - document.activeElement.should.equal endInput - - describe 'when the end input is focused', -> - - it 'should set the focus to the start input', -> - - endInput.focus() - dateRangePicker.pick(dateRange) - document.activeElement.should.equal startInput - - -describe 'DateRangePicker (options)', -> - - jsdom() - - form = null - input = null - dateRangePicker = null - - form = null - startInput = null - endInput = null - dateRangePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - startInput = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-range-picker': true - } - ) - startInput.getBoundingClientRect = -> - return { - bottom: 20, - height: 10, - left: 10, - right: 110, - top: 10, - width: 100 - } - endInput = $.create('input', {'value': '5 Jan 2000'}) - endInput.getBoundingClientRect = -> - return { - bottom: 20, - height: 10, - left: 50, - right: 150, - top: 10, - width: 100 - } - document.body.appendChild(form) - form.appendChild(startInput) - form.appendChild(endInput) - - afterEach -> - document.body.removeChild(dateRangePicker._dom.picker) - - describe 'closeOnPick', -> - - describe 'when false', -> - - before -> - dateRangePicker = new DateRangePicker( - startInput, - endInput, - {closeOnPick: false} - ) - - it 'should keep the date picker open once a date has been - picked', -> - - dateRangePicker.open() - dateRangePicker.pick([ - new Date(2000, 0, 5), - new Date(2000, 0, 10) - ]) - dateRangePicker.isOpen.should.be.true - - describe 'when true', -> - - before -> - dateRangePicker = new DateRangePicker( - startInput, - endInput, - {closeOnPick: true} - ) - - it 'should close the date picker once a date has been picked', -> - - dateRangePicker.open() - dateRangePicker.pick([ - new Date(2000, 0, 5), - new Date(2000, 0, 10) - ]) - dateRangePicker.isOpen.should.be.false - - describe 'format', -> - - describe 'when iso', -> - - before -> - dateRangePicker = new DateRangePicker( - startInput, - endInput, - {format: 'iso'} - ) - - it 'should set the input values as ISO 8601 format dates when a date - range is picked', -> - - dateRangePicker.pick([ - new Date(2000, 0, 5), - new Date(2000, 0, 10) - ]) - startInput.value.should.equal '2000-01-05' - endInput.value.should.equal '2000-01-10' - - describe 'parsers', -> - - describe 'when ["mdy"]', -> - - before -> - dateRangePicker = new DateRangePicker( - startInput, - endInput, - { - format: 'mdy', - parsers: ['mdy'] - } - ) - - it 'should only parse dates in m/d/y format when the input value is - changed', -> - - # Date should not be parsed and therefore the selected date and - # input value should not have changed. - startInput.value = '2001-01-05' - endInput.value = '2001-01-10' - $.dispatch(startInput, 'changed') - $.dispatch(endInput, 'changed') - - startInput.value.should.equal '2001-01-05' - endInput.value.should.equal '2001-01-10' - - # Date should be parsed and therefore the selected date and - # input should have changed. - startInput.value = '05/01/2001' - endInput.value = '10/01/2001' - $.dispatch(startInput, 'changed') - $.dispatch(endInput, 'changed') - - startInput.value.should.equal '05/01/2001' - endInput.value.should.equal '10/01/2001' - - describe 'pinToStart', -> - - describe 'when true and the end input is focused', -> - - before -> - dateRangePicker = new DateRangePicker( - startInput, - endInput, - {'pinToStart': true} - ) - - it 'should position the date range picker in-line with the start - input', -> - - endInput.focus() - - pickerElm = dateRangePicker._dom.picker - pickerElm.style.left.should.equal '10px' - pickerElm.style.top.should.equal '20px' - - -describe 'DateRangePicker (behaviours)', -> - - jsdom() - - form = null - startInput = null - endInput = null - dateRangePicker = null - - before -> - # Build a form with an input field - form = $.create('form') - startInput = $.create( - 'input', - { - 'value': '1 Jan 2000', - 'data-mh-date-range-picker': true - } - ) - endInput = $.create('input', {'value': '5 Jan 2000'}) - document.body.appendChild(form) - form.appendChild(startInput) - form.appendChild(endInput) - - # Initialize a date range picker for the input fields - dateRangePicker = new DateRangePicker(startInput, endInput) - - describe 'input', -> - - dateRange = [new Date(2001, 1, 5), new Date(2001, 1, 10)] - - describe 'set-value', -> - - before -> - startInput.value = '' - endInput.value = '' - - it 'should set the value of the start and end input fields', -> - - behaviour = DateRangePicker.behaviours.input['set-value'] - behaviour(dateRangePicker, dateRange) - startInput.value.should.equal '5 February 2001' - endInput.value.should.equal '10 February 2001' \ No newline at end of file diff --git a/coffee/webpack.config.js b/coffee/webpack.config.js deleted file mode 100644 index e3b4563..0000000 --- a/coffee/webpack.config.js +++ /dev/null @@ -1,118 +0,0 @@ -// Imports -const path = require('path') -const webpack = require('webpack') - -// Plugin config -const plugins = [ - - new webpack.DefinePlugin({ - 'process.env': { - - // The `NODE_ENV` flag indicates which environment web pack is compiling - // in/for (defaults to the local environment). - 'NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'dev') - - } - }), - - new webpack.optimize.DedupePlugin() -]; - -// Environment specific config -switch (process.env.NODE_ENV) { - - case 'dist': - var uglifyPlugin = new webpack.optimize.UglifyJsPlugin({ - beautify: false, - comments: false, - compress: { - drop_console: true, - warnings: false - }, - mangle: { - except: ['webpackJsonp'], - screw_ie8 : true, - keep_fnames: true - } - }) - - plugins.push(uglifyPlugin); - break; - - default: - break; - -} - -// Project config -module.exports = { - plugins: plugins, - - entry: { - 'index': [ - path.resolve(__dirname, 'src/styles', 'date-picker.scss'), - path.resolve(__dirname, 'src/scripts', 'date-picker.coffee') - ] - }, - - output: { - library: 'ManhattanDatePicker', - libraryTarget: 'umd', - path: path.join(__dirname, 'dist'), - filename: 'index.js' - }, - - module: { - preLoaders: [ - // CoffeeScript (lint) - { - test: /\.coffee$/, - exclude: /node_modules/, - loader: 'coffeelint-loader' - }, - - // SASS (lint) - { - test: /\.scss$/, - exclude: /node_modules/, - loader: 'sasslint' - } - ], - - loaders: [ - // CoffeeScript (to JavaScript) - { - test: /\.coffee$/, - loaders: ['coffee'] - }, - - // SASS (to CSS) - { - test: /\.scss$/, - loaders: [ - 'file?name=date-picker.css', - 'extract', - 'css', - 'sass' - ] - } - ] - }, - - stats: { - // Prevent duplicate output of SASS lint reports - children: false - }, - - // Loader config - sasslint: { - configFile: '.sass-lint.yml' - }, - - // Dev server - devServer: { - contentBase: path.resolve(__dirname, 'dist'), - inline: true, - port: 5999 - } -}; \ No newline at end of file