diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..325be86 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + + +## 0.2.0 (Oct 18, 2015) + +- Supports custom events +- Supports to set start view date and end view date +- Improved i18n (internationalization) +- Improved placement of the datepicker +- Improved template + + +### Options + +- Added 10 options: `autoshow`, `autopick`, `offset`, `language`, `startDate`, `endDate`, `mutedClass`, `show`, `hide`, `pick` +- Renamed `autoclose` to `autohide` +- Renamed `dateFormat` to `format` +- Renamed `viewStart` to `startView` +- Renamed `showMonthAfterYear` to `yearFirst` +- Renamed `selectedClass` to `pickedClass` +- Renamed `isDisabled` to `filter` + + +### Methods + +- Added 11 methods: `pick`, `reset`, `getMonthName`, `getDayName`, `getDate`, `setDate`, `setStartDate`, `setEndDate`, `parseDate`, `formatDate`, `destroy` +- Removed 2 methods: `enable`, `disable` + + +### Events + +- Added 3 events: `show.datepicker`, `hide.datepicker`, `pick.datepicker` + + +## 0.1.0 (Aug 9, 2014) + +- Fixed some bugs +- Added i18n files +- Optimized code style + + +## 0.1.0-beta (Feb 14, 2014) + +- Supports 21 options: `date`, `dateFormat`, `disabledClass`, `selectedClass`, `autoclose`, `inline`, `trigger`, `container`, `showMonthAfterYear`, `zIndex`, `viewStart`, `weekStart`, `yearSuffix`, `days`, `daysShort`, `daysMin`, `months`, `monthsShort`, `itemTag`, `template`, `isDisabled` +- Supports 5 methods: `show`, `hide`, `enable`, `disable`, `update` diff --git a/README.md b/README.md index a001bfe..8605af2 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,46 @@ # [Datepicker](http://fengyuanchen.github.io/datepicker) -A simple, lightweight, customizable jQuery datepicker plugin. +> A simple jQuery datepicker plugin. -- [Documentation](http://fengyuanchen.github.io/datepicker) +- [Demo](http://fengyuanchen.github.io/datepicker) -# Getting started +## Features -## Installation +- Supports [options](#options) +- Supports [methods](#methods) +- Supports [events](#events) +- Supports inline +- Supports mobile +- Cross-browser support + + + +## Main + +``` +dist/ +├── datepicker.css ( 4 KB) +├── datepicker.min.css ( 4 KB) +├── datepicker.js (37 KB) +└── datepicker.min.js (15 KB) +``` + + + +## Getting started + +### Quick start + +Three quick start options are available: + +- [Download the latest release](https://github.com/fengyuanchen/datepicker/archive/master.zip). +- Clone the repository: `git clone https://github.com/fengyuanchen/datepicker.git`. +- Install with [NPM](http://npmjs.org): `npm install fengyuanchen@datepicker`. + + +### Installation Include files: @@ -19,255 +51,625 @@ Include files: ``` -## Usage - -### Initialize with `datepicker` attribute +### Usage -#### Basic: +Initialize with `$.fn.datepicker` method. ```html - + + +
+``` + +```js +$('[data-toggle="datepicker"]').datepicker(); ``` -#### Add options with `data-*` attribute -```html - +## Options + +You may set datepicker options with `$().datepicker(options)`. +If you want to change the global default options, You may use `$.fn.datepicker.setDefaults(options)`. + + +### autoshow + +- Type: `Boolean` +- Default: `false` + +Show the datepicker automatically when initialized. + + +### autohide + +- Type: `Boolean` +- Default: `false` + +Hide the datepicker automatically when picked. + + +### autopick + +- Type: `Boolean` +- Default: `false` + +Pick the initial date automatically when initialized. + + +### inline + +- Type: `Boolean` +- Default: `false` + +Enable inline mode. + +If the element is not an input element, will append the datepicker to the element. +If the `container` option is set, will append the datepicker to the container. + + +### container + +- Type: `Element` or `String`(selector) +- Default: `null` + +A element for putting the datepicker. If not set, the datepicker will be appended to `document.body` by default. + + +### trigger + +- Type: `Element` or `String`(selector) +- Default: `null` + +A element for triggering the datepicker. + + +### language + +- Type: `String` +- Default: `''` + +The ISO language code. If not set, will use the built-in language (en-US) by default. + +```js +$().datepicker({ + language: 'en-GB' +}); ``` -### Initialize with `$.fn.datepicker` method -#### Basic: +### format -```html - +- Type: `String` +- Default: `'mm/dd/yyyy'` +- Available date placeholders: + - Year: `yyyy`, `yy` + - Month: `mm`, `m` + - Day: `dd`, `d` + +The date string format. + +```js +$().datepicker({ + format: 'yyyy-mm-dd' +}); ``` -```javascript -$(".datepicker").datepicker(); + +### date + +- Type: `Date` or `String` +- Default: `null` + +The initial date. If not set, will use the current date by default. + +```js +$().datepicker({ + date: new Date(2014, 1, 14) // Or '02/14/2014' +}); ``` -#### Add options + +### startDate + +- Type: `Date` or `String` +- Default: `null` + +The start view date. All the dates before this date will be disabled. + + +### endDate + +- Type: `Date` or `String` +- Default: `null` + +The end view date. All the dates after this date will be disabled. + + +### startView + +- Type: `Number` +- Default: `0` +- Options: + - `0`: days + - `1`: months + - `2`: years + +The start view when initialized. + + +### weekStart + +- Type: `Number` +- Default: `0` +- Options: + - `0`: Sunday + - `1`: Monday + - `2`: Tuesday + - `3`: Wednesday + - `4`: Thursday + - `5`: Friday + - `6`: Saturday + +The start day of the week. + + +### yearFirst + +- Type: `Boolean` +- Default: `false` + +Show year before month on the datepicker header + + +### yearSuffix + +- Type: `String` +- Default: `''` + +A string suffix to the year number. + +```js +$().datepicker({ + yearSuffix: '年' +}); +``` + +### days + +- Type: `Array` +- Default: `['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']` + +Days' name of the week. + + +### daysShort + +- Type: `Array` +- Default: `['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']` + +Shorter days' name. + + +### daysMin + +- Type: `Array` +- Default: `['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']` + +Shortest days' name. + + +### months + +- Type: `Array` +- Default: `['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']` + +Months' name. + + +### monthsShort + +- Type: `Array` +- Default: `['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']` + +Shorter months' name. + + +### itemTag + +- Type: `String` +- Default: `'li'` + +A element tag for each item of years, months and days. + + +### mutedClass + +- Type: `String` +- Default: `'muted'` + +A class (CSS) for muted item. + + +### pickedClass + +- Type: `String` +- Default: `'picked'` + +A class (CSS) for picked item. + + +### disabledClass + +- Type: `String` +- Default: `'disabled'` + +A class (CSS) for disabled item. + + +### template + +- Type: `String` +- Default: ```html - +
+
+ + +
+
+ + +
+
+ + + +
+
``` -```javascript -$("#datepicker-add-options").datepicker({ - dateFormat: "yyyy.m.d", - weekStart: 1 +The template of the datepicker. + +**Note:** All the `data-view` attributes must be set when you customize it. + + +### offset + +- Type: `Number` +- Default: `10` + +The offset top or bottom of the datepicker from the element. + + +### zIndex + +- Type: `Number` +- Default: `1` + +The CSS `z-index` style for the datepicker. + + +### filter + +- Type: `Function` +- Default: `null` + +Filter each date item. If return a `false` value, the related date will be disabled. + +```js +var now = Date.now(); + +$().datepicker({ + filter: function(date) { + if (date.getDay() === 0) { + return false; // Disable all Sundays + } + } }); ``` -## Options +### show -Setup with `$("#target").datepicker(options)`, or global setup with `$.fn.datepicker.setDefaults(options)`. +- Type: `Function` +- Default: `null` -#### autoClose +A shortcut of the "show.datepicker" event. -* type: boolean -* default: false -Close the picker when a date was selected. +### hide +- Type: `Function` +- Default: `null` -#### dateFormat +A shortcut of the "hide.datepicker" event. -* type: string -* default: "mm/dd/yyyy" -example: "yyyy-mm-dd", "yy.m.d" +### pick +- Type: `Function` +- Default: `null` -#### viewStart +A shortcut of the "pick.datepicker" event. -* type: number -* default: 0 -0 for "days", 1 for "months", 2 for "years". +## Methods -#### weekStart +Common usage: -* type: number -* default: 0 +```js +$().datepicker('method', argument1, , argument2, ..., argumentN); +``` -0 for Sunday, 1 for Monday, 3 for Tuesday, 4 for Thursday, 5 for Friday, 6 for Saturday. +### show() -#### showMonthAfterYear +Show the datepicker. -* type: boolean -* default: false +### hide() -#### yearSuffix +Hide the datepicker. -* type: string -* default: "" -example: "年" +### update() +Update the datepicker with the value or text of the current element. -#### months -* type: array -* default: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] +### pick() +Pick the current date to the element. -#### monthsShort -* type: array -* default: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] +### reset() +Reset the datepicker. -#### days -* type: array -* default: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] +### getMonthName([month[, short]]) +- **month** (optional): + - Type: `Number` + - Default: the month of the current date -#### daysShort +- **short** (optional): + - Type: `Boolean` + - Default: `false` + - Get the shorter month name -* type: array -* default: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] +- (return value): + - Type: `String` +Get the month name with given argument or the current date. -#### daysMin +```js +$().datepicker('getMonthName'); // 'January' +$().datepicker('getMonthName', true); // 'Jan' +$().datepicker('getMonthName', 11); // 'December' +$().datepicker('getMonthName', 11, true); // 'Dec' +``` -* type: array -* default: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] +### getDayName([day[, short[, min]]) -#### itemTag +- **day** (optional): + - Type: `Number` + - Default: the day of the current date -* type: string -* default: "li" +- **short** (optional): + - Type: `Boolean` + - Default: `false` + - Get the shorter day name -years, months, days items element tag. +- **min** (optional): + - Type: `Boolean` + - Default: `false` + - Get the shortest day name +- (return value): + - Type: `String` -#### selectedClass +Get the day name with given argument or the current date. -* type: string -* default: "datepicker-selected" +```js +$().datepicker('getDayName'); // 'Sunday' +$().datepicker('getDayName', true); // 'Sun' +$().datepicker('getDayName', true, true); // 'Su' +$().datepicker('getDayName', 6); // 'Saturday' +$().datepicker('getDayName', 6, true); // 'Sat' +$().datepicker('getDayName', 6, true, true); // 'Sa' +``` -#### disabledClass +### getDate([formatted]) -* type: string -* default: "datepicker-disabled" +- **formatted** (optional): + - Type: `Boolean` + - Default: `false` + - Get a formatted date string +- (return value): + - Type: `Date` or `String` -#### isDisabled +Get the current date. -* type: function -* default: `function() { return false; }` -* param: date object -* return: boolean +```js +$().datepicker('getDate'); // date object +$().datepicker('getDate', true); // '02/14/2014' +``` -For example: -```javascript -var now = Date.now(); +### setDate(date) -$(".datepicker").datepicker({ - isDisabled: function(date) { - return date.valueOf() < now ? true : false; - } -}) +- **date**: + - Type: `Date` or `String` + +Set the current date with a new date. + +```js +$().datepicker('setDate', new Date(2014, 1, 14)); +$().datepicker('setDate', '02/14/2014'); ``` -#### template - -* type: string -* default: - -```javascript -[ - '
', - '
', - '
', - '
', - '
    ', - '
  • ', - '
  • ', - '
  • ', - '
', - '
    ', - '
    ', - '
    ', - '
      ', - '
    • ', - '
    • ', - '
    • ', - '
    ', - '
      ', - '
      ', - '
      ', - '
        ', - '
      • ', - '
      • ', - '
      • ', - '
      ', - '
        ', - '
          ', - '
          ', - '
          ', - '
          ' -].join("") +### setStartDate(date) + +- **date**: + - Type: `Date` or `String` + +Set the start view date with a new date. + + +### setEndDate(date) + +- **date**: + - Type: `Date` or `String` + +Set the end view date with a new date. + + +### parseDate(date) + +- **date**: + - Type: `String` + +Parse a date string with the set date format. + +```js +$().datepicker('parseDate', '02/14/2014'); // date object ``` -/!\ All the data-type="" attributes must be set when you customize it. +### formatDate(date) +- **date**: + - Type: `Date` -## Methods +Format a date object to a string with the set date format. -* show - Show the datepicker -* hide - Hide the datepicker -* enable - Enable the datepicker -* disable - Disable the datepicker -* update - Update the datepicker +```js +$().datepicker('formatDate', new Date(2014, 1, 14)); // '02/14/2014' +``` +### destroy() + +Destroy the datepicker and remove the instance from the target element. + + + +## Events + +### show.datepicker + +This event fires when starts to show the datepicker. + + +### hide.datepicker + +This event fires when starts to hide the datepicker. -## I18n -For example: - -```javascript -// For Chinese (zh-CN) -$.fn.datepicker.setDefaults({ - autoClose: false, - dateFormat: "yyyy-mm-dd", - days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"], - daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"], - daysMin: ["日", "一", "二", "三", "四", "五", "六"], - months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], - monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"], - showMonthAfterYear: true, - viewStart: 0, - weekStart: 1, - yearSuffix: "年" +### pick.datepicker + +- **event.date**: + - Type: `Date` + - The current date + +- **event.view**: + - Type: `String` + - Default: `''` + - Options: `'year'`, `'month'`, `'day'` + - The current visible view + +This event fires when start to pick a year, month or day. + +```js +$().on('pick.datepicker', function (e) { + if (e.date < new Date()) { + e.preventDefault(); // Prevent to pick the date + } }); ``` -## Browser Support -- IE 6+ -- Chrome 33+ -- Firefox 27+ -- Safari 5.1+ -- Opera 19+ +## I18n + +### Config + +```js +// datepicker.zh-CN.js +$.fn.datepicker.languages['zh-CN'] = { + format: 'yyyy年mm月dd日', + days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + daysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], + daysMin: ['日', '一', '二', '三', '四', '五', '六'], + months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + weekStart: 1, + startView: 0, + yearFirst: true, + yearSuffix: '年' +}; +``` + +### Usage + +```html + + + +``` + + + +## No conflict + +If you have to use other plugin with the same namespace, just call the `$.fn.datepicker.noConflict` method to revert to it. + +```html + + + +``` + + + +## Browser support + +- Chrome (latest 2) +- Firefox (latest 2) +- Internet Explorer 8+ +- Opera (latest 2) +- Safari (latest 2) + +As a jQuery plugin, you also need to see the [jQuery Browser Support](http://jquery.com/browser-support/). -As a jQuery plugin, you can reference to the [jQuery Browser Support](http://jquery.com/browser-support/). -## [License](https://github.com/fengyuanchen/datepicker/blob/master/LICENSE.md) +## [License](LICENSE.md) -Released under the [MIT](http://opensource.org/licenses/mit-license.html) license. +[MIT](http://opensource.org/licenses/MIT) © [Fengyuan Chen](http://github.com/fengyuanchen) diff --git a/dist/datepicker.css b/dist/datepicker.css index 40fd092..4e58088 100644 --- a/dist/datepicker.css +++ b/dist/datepicker.css @@ -1,138 +1,178 @@ /*! - * Datepicker v0.1.0 + * Datepicker v0.2.0 * https://github.com/fengyuanchen/datepicker * - * Copyright 2014 Fengyuan Chen + * Copyright (c) 2014-2015 Fengyuan Chen * Released under the MIT license + * + * Date: 2015-10-18T06:04:18.882Z */ - .datepicker-container { - position: absolute; - z-index: 201312; - float: left; - font-family: arial, helvetica, sans-serif; - font-size: 12px; - line-height: 30px; -} - -.datepicker-arrow, -.datepicker-arrow:after { - display: block; - width: 0; - height: 0; - border: 5px solid transparent; -} - -.datepicker-arrow { - position: relative; - margin: 0 10px; - border-bottom-color: #9cf; -} - -.datepicker-arrow:after { - position: absolute; - top: -4px; - left: -5px; - content: " "; - border-bottom-color: #fff; -} - -.datepicker-content { - width: 210px; - -webkit-box-sizing: content-box; - box-sizing: content-box; - border: 1px solid #ccc; - border-top-color: #9cf; - -webkit-box-shadow: 0 0 3px #ccc; - box-shadow: 0 0 3px #ccc; -} - -.datepicker-prev, -.datepicker-next { - font-size: 18px; -} - -.datepicker-content ul:before, -.datepicker-content ul:after { - display: table; - content: ""; -} - -.datepicker-content ul:after { - clear: both; -} - -.datepicker-content ul { - width: 102%; - padding: 0; - margin: 0; - - *zoom: 1; -} - -.datepicker-content li { - float: left; - width: 30px; - height: 30px; - padding: 0; - margin: 0; - text-align: center; - list-style: none; - cursor: pointer; - background-color: #fff; -} - -.datepicker-content .col-1 { - width: 30px; -} - -.datepicker-content .col-2 { - width: 60px; -} - -.datepicker-content .col-3 { - width: 90px; -} - -.datepicker-content .col-4 { - width: 120px; -} - -.datepicker-content .col-5 { - width: 150px; -} - -.datepicker-content .col-6 { - width: 180px; -} - -.datepicker-content .col-7 { - width: 210px; -} - -.datepicker-content li:hover { - background-color: #eee; -} - -.datepicker-content .selected, -.datepicker-content .selected:hover { - color: #36f; -} - -.datepicker-content .disabled, -.datepicker-content .disabled:hover { - color: #ccc; -} - -.datepicker-years li, -.datepicker-months li { - width: 52.5px; - height: 52.5px; - line-height: 52.5px; -} - -.datepicker-week li, -.datepicker-week li:hover { - cursor: default; - background-color: #fff; + position: fixed; + top: 0; + left: 0; + z-index: -1; + width: 210px; + font-size: 12px; + line-height: 30px; + -ms-touch-action: none; + touch-action: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #fff; + + direction: ltr !important; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; +} +.datepicker-container:before, + .datepicker-container:after { + position: absolute; + display: block; + width: 0; + height: 0; + content: " "; + border: 5px solid transparent; +} + +.datepicker-dropdown { + position: absolute; + z-index: 1; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + border: 1px solid #ccc; + -webkit-box-shadow: 0 3px 6px #ccc; + box-shadow: 0 3px 6px #ccc; +} + +.datepicker-inline { + position: static; +} + +.datepicker-top-left, +.datepicker-top-right { + border-top-color: #39f; +} +.datepicker-top-left:before, + .datepicker-top-left:after, + .datepicker-top-right:before, + .datepicker-top-right:after { + top: -5px; + left: 10px; + border-top: 0; +} +.datepicker-top-left:before, + .datepicker-top-right:before { + border-bottom-color: #39f; +} +.datepicker-top-left:after, + .datepicker-top-right:after { + top: -4px; + border-bottom-color: #fff; +} + +.datepicker-bottom-left, +.datepicker-bottom-right { + border-bottom-color: #39f; +} +.datepicker-bottom-left:before, + .datepicker-bottom-left:after, + .datepicker-bottom-right:before, + .datepicker-bottom-right:after { + bottom: -5px; + left: 10px; + border-bottom: 0; +} +.datepicker-bottom-left:before, + .datepicker-bottom-right:before { + border-top-color: #39f; +} +.datepicker-bottom-left:after, + .datepicker-bottom-right:after { + bottom: -4px; + border-top-color: #fff; +} + +.datepicker-top-right:before, +.datepicker-top-right:after, +.datepicker-bottom-right:before, +.datepicker-bottom-right:after { + right: 10px; + left: auto; +} + +.datepicker-panel > ul:before, +.datepicker-panel > ul:after { + display: table; + content: " "; +} + +.datepicker-panel > ul:after { + clear: both; +} + +.datepicker-panel > ul { + width: 102%; + padding: 0; + margin: 0; +} +.datepicker-panel > ul > li { + float: left; + width: 30px; + height: 30px; + padding: 0; + margin: 0; + text-align: center; + list-style: none; + cursor: pointer; + background-color: #fff; +} +.datepicker-panel > ul > li:hover { + background-color: #eee; +} +.datepicker-panel > ul > li.muted, + .datepicker-panel > ul > li.muted:hover { + color: #999; +} +.datepicker-panel > ul > li.picked, + .datepicker-panel > ul > li.picked:hover { + color: #39f; +} +.datepicker-panel > ul > li.disabled, + .datepicker-panel > ul > li.disabled:hover { + color: #ccc; + cursor: default; + background-color: #fff; +} +.datepicker-panel > ul > li[data-view="years prev"], + .datepicker-panel > ul > li[data-view="year prev"], + .datepicker-panel > ul > li[data-view="month prev"], + .datepicker-panel > ul > li[data-view="years next"], + .datepicker-panel > ul > li[data-view="year next"], + .datepicker-panel > ul > li[data-view="month next"], + .datepicker-panel > ul > li[data-view="next"] { + font-size: 18px; +} +.datepicker-panel > ul > li[data-view="years current"], + .datepicker-panel > ul > li[data-view="year current"], + .datepicker-panel > ul > li[data-view="month current"] { + width: 150px; +} +.datepicker-panel > ul[data-view="years"] > li, + .datepicker-panel > ul[data-view="months"] > li { + width: 52.5px; + height: 52.5px; + line-height: 52.5px; +} +.datepicker-panel > ul[data-view="week"] > li, + .datepicker-panel > ul[data-view="week"] > li:hover { + cursor: default; + background-color: #fff; +} + +.datepicker-hide { + display: none; } diff --git a/dist/datepicker.js b/dist/datepicker.js index 43d6050..3151461 100644 --- a/dist/datepicker.js +++ b/dist/datepicker.js @@ -1,733 +1,1464 @@ /*! - * Datepicker v0.1.0 + * Datepicker v0.2.0 * https://github.com/fengyuanchen/datepicker * - * Copyright 2014 Fengyuan Chen + * Copyright (c) 2014-2015 Fengyuan Chen * Released under the MIT license + * + * Date: 2015-10-18T06:04:17.960Z */ (function (factory) { - if (typeof define === "function" && define.amd) { - // AMD. Register as anonymous module. - define(["jquery"], factory); - } else { - // Browser globals. - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define('datepicker', ['jquery'], factory); + } else if (typeof exports === 'object') { + // Node / CommonJS + factory(require('jquery')); + } else { + // Browser globals. + factory(jQuery); + } })(function ($) { - "use strict"; + 'use strict'; + + var $window = $(window); + var document = window.document; + var $document = $(document); + var NAMESPACE = 'datepicker'; + + // Events + var EVENT_CLICK = 'click.' + NAMESPACE; + var EVENT_KEYUP = 'keyup.' + NAMESPACE; + var EVENT_FOCUS = 'focus.' + NAMESPACE; + var EVENT_RESIZE = 'resize.' + NAMESPACE; + var EVENT_SHOW = 'show.' + NAMESPACE; + var EVENT_HIDE = 'hide.' + NAMESPACE; + var EVENT_PICK = 'pick.' + NAMESPACE; + + // RegExps + var REGEXP_FORMAT = /y+|m+|d+/g; + var REGEXP_DIGITS = /\d+/g; + var REGEXP_YEAR = /^\d{2,4}$/; + + // Classes + var CLASS_INLINE = 'datepicker-inline'; + var CLASS_DROPDOWN = 'datepicker-dropdown'; + var CLASS_TOP_LEFT = 'datepicker-top-left'; + var CLASS_TOP_RIGHT = 'datepicker-top-right'; + var CLASS_BOTTOM_LEFT = 'datepicker-bottom-left'; + var CLASS_BOTTOM_RIGHT = 'datepicker-bottom-right'; + var CLASS_PLACEMENTS = [ + CLASS_TOP_LEFT, + CLASS_TOP_RIGHT, + CLASS_BOTTOM_LEFT, + CLASS_BOTTOM_RIGHT + ].join(' '); + var CLASS_HIDE = 'datepicker-hide'; + + // Maths + var num = Number; + var min = Math.min; + + // Utilities + var toString = Object.prototype.toString; + + function isString(str) { + return typeof str === 'string'; + } + + function isNumber(num) { + return typeof num === 'number' && !isNaN(num); + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDate(date) { + if (typeof date !== 'object' || date === null) { + return false; + } - var $window = $(window), - $document = $(document), - Datepicker = function (element, options) { - this.$element = $(element); - this.defaults = $.extend({}, Datepicker.defaults, this.$element.data(), $.isPlainObject(options) ? options : {}); - this.init(); - }; + return toString.call(date) === '[object Date]'; + } - Datepicker.prototype = { - constructor: Datepicker, - - init: function () { - var trigger = this.defaults.trigger; - - this.$trigger = trigger ? $(trigger) : this.$element; - this.$picker = $(this.defaults.template); - this.$years = this.$picker.find("[data-type='years picker']"); - this.$months = this.$picker.find("[data-type='months picker']"); - this.$days = this.$picker.find("[data-type='days picker']"); - this.$picker.appendTo("body"); - this.place(); - this.hide(); - - this.format = Datepicker.fn.parseFormat(this.defaults.dateFormat); - this.fillWeek(); - this.enable(); - }, - - enable: function () { - if (this.enabled) { - return; - } + function toArray(obj, offset) { + var args = []; - if (this.$element.is("input")) { - this.$element.on("keyup", $.proxy(this.update, this)); + // This is necessary for IE8 + if (isNumber(offset)) { + args.push(offset); + } - if (!this.defaults.trigger) { - this.$element.on("focus", $.proxy(this.show, this)); - } - } + return args.slice.apply(obj, args); + } - this.$trigger.on("click", $.proxy(this.show, this)); + // Custom proxy to avoid jQuery's guid + function proxy(fn, context) { + var args = toArray(arguments, 2); - this.$picker.on({ - click: $.proxy(this.click, this), - mousedown: $.proxy(this.mousedown, this) - }); + return function () { + return fn.apply(context, args.concat(toArray(arguments))); + }; + } - this.update(); - this.enabled = true; - }, + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } - disable: function () { - if (!this.enabled) { - return; - } + function getDaysInMonth(year, month) { + return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; + } - if (this.$element.is("input")) { - this.$element.off("keyup", this.update); + function parseFormat(format) { + var source = String(format).toLowerCase(); + var parts = source.match(REGEXP_FORMAT); + var length; + var i; - if (!this.defaults.trigger) { - this.$element.off("focus", this.show); - } - } + if (!parts || parts.length === 0) { + throw new Error('Invalid date format.'); + } - this.$trigger.off("click", this.show); - - this.$picker.off({ - click: this.click, - mousedown: this.mousedown - }); - - this.hide(); - this.enabled = false; - }, - - showView: function (type) { - var format = this.format; - - if (format.year || format.month || format.day) { - switch (type) { - - case 2: - case "years": - this.$months.hide(); - this.$days.hide(); - - if (format.year) { - this.fillYears(); - this.$years.show(); - } else { - this.showView(0); - } - - break; - - case 1: - case "months": - this.$years.hide(); - this.$days.hide(); - - if (format.month) { - this.fillMonths(); - this.$months.show(); - } else { - this.showView(2); - } - - break; - - // case 0: - // case "days": - default: - this.$years.hide(); - this.$months.hide(); - - if (format.day) { - this.fillDays(); - this.$days.show(); - } else { - this.showView(1); - } - } - } - }, + format = { + source: source, + parts: parts + }; - hideView: function () { - if (this.defaults.autoClose) { - this.hide(); - } - }, + length = parts.length; - place: function () { - var offset = this.$trigger.offset(), - height = this.$trigger.outerHeight(); + for (i = 0; i < length; i++) { + switch (parts[i]) { + case 'dd': + case 'd': + format.hasDay = true; + break; - this.$picker.css({ - top: offset.top + height, - left: offset.left - }); - }, + case 'mm': + case 'm': + format.hasMonth = true; + break; - show: function () { - if (!this.enabled) { - return; - } + case 'yyyy': + case 'yy': + format.hasYear = true; + break; + + // No default + } + } + + return format; + } + + function Datepicker(element, options) { + this.$element = $(element); + this.options = $.extend({}, Datepicker.DEFAULTS, $.isPlainObject(options) && options); + this.isBuilt = false; + this.isShown = false; + this.isInput = false; + this.isInline = false; + this.initialValue = ''; + this.initialDate = null; + this.startDate = null; + this.endDate = null; + this.init(); + } + + Datepicker.prototype = { + constructor: Datepicker, + + version: '0.2.0', + + init: function () { + var options = this.options; + var $this = this.$element; + var startDate = options.startDate; + var endDate = options.endDate; + var date = options.date; + + if (options.language) { + $.extend(options, Datepicker.LANGUAGES[options.language]); + } + + this.$trigger = $(options.trigger || $this); + this.isInput = $this.is('input') || $this.is('textarea'); + this.isInline = options.inline && (options.container || !this.isInput); + this.format = parseFormat(options.format); + this.initialValue = this.getValue(); + date = this.parseDate(date || this.initialValue); + + if (startDate) { + startDate = this.parseDate(startDate); + + if (date.getTime() < startDate.getTime()) { + date = new Date(startDate); + } + + this.startDate = startDate; + } + + if (endDate) { + endDate = this.parseDate(endDate); - this.$picker.show(); - $window.on("resize", $.proxy(this.place, this)); - $document.on("mousedown", $.proxy(this.hide, this)); + if (startDate && endDate.getTime() < startDate.getTime()) { + endDate = new Date(startDate); + } + + if (date.getTime() > endDate.getTime()) { + date = new Date(endDate); + } - this.place(); - this.showView(this.defaults.viewStart); - }, + this.endDate = endDate; + } + + this.date = date; + this.viewDate = new Date(date); + this.initialDate = new Date(this.date); + + this.bind(); + + if (options.autoshow || this.isInline) { + this.show(); + } + + if (options.autopick) { + this.pick(); + } + }, + + build: function () { + var options = this.options; + var $this = this.$element; + var $picker; + + if (this.isBuilt) { + return; + } + + this.isBuilt = true; + + this.$picker = $picker = $(options.template); + this.$week = $picker.find('[data-view="week"]'); + + // Years view + this.$yearsPicker = $picker.find('[data-view="years picker"]'); + this.$yearsPrev = $picker.find('[data-view="years prev"]'); + this.$yearsNext = $picker.find('[data-view="years next"]'); + this.$yearsCurrent = $picker.find('[data-view="years current"]'); + this.$years = $picker.find('[data-view="years"]'); + + // Months view + this.$monthsPicker = $picker.find('[data-view="months picker"]'); + this.$yearPrev = $picker.find('[data-view="year prev"]'); + this.$yearNext = $picker.find('[data-view="year next"]'); + this.$yearCurrent = $picker.find('[data-view="year current"]'); + this.$months = $picker.find('[data-view="months"]'); + + // Days view + this.$daysPicker = $picker.find('[data-view="days picker"]'); + this.$monthPrev = $picker.find('[data-view="month prev"]'); + this.$monthNext = $picker.find('[data-view="month next"]'); + this.$monthCurrent = $picker.find('[data-view="month current"]'); + this.$days = $picker.find('[data-view="days"]'); + + if (this.isInline) { + $(options.container || $this).append($picker.addClass(CLASS_INLINE)); + } else { + $(document.body).append($picker.addClass(CLASS_DROPDOWN)); + $picker.addClass(CLASS_HIDE); + } + + this.fillWeek(); + }, + + unbuild: function () { + if (!this.isBuilt) { + return; + } + + this.isBuilt = false; + this.$picker.remove(); + }, + + bind: function () { + var options = this.options; + var $this = this.$element; + + if ($.isFunction(options.show)) { + $this.on(EVENT_SHOW, options.show); + } + + if ($.isFunction(options.hide)) { + $this.on(EVENT_HIDE, options.hide); + } + + if ($.isFunction(options.pick)) { + $this.on(EVENT_PICK, options.pick); + } + + if (this.isInput) { + $this.on(EVENT_KEYUP, $.proxy(this.keyup, this)); + + if (!options.trigger) { + $this.on(EVENT_FOCUS, $.proxy(this.show, this)); + } + } - hide: function () { - this.$picker.hide(); - $window.off("resize", this.place); - $document.off("mousedown", this.hide); - }, + this.$trigger.on(EVENT_CLICK, $.proxy(this.show, this)); + }, - mousedown: function (e) { - e.stopPropagation(); - e.preventDefault(); - }, + unbind: function () { + var options = this.options; + var $this = this.$element; - update: function () { - var viewDate = this.$element.is("input") ? this.$element.prop("value") : this.$element.text(); + if ($.isFunction(options.show)) { + $this.off(EVENT_SHOW, options.show); + } - this.date = Datepicker.fn.parseDate(viewDate, this.format); - this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), this.date.getDate(), 0, 0, 0, 0); - this.fillAll(); - }, + if ($.isFunction(options.hide)) { + $this.off(EVENT_HIDE, options.hide); + } - output: function () { - var $element = this.$element, - date = Datepicker.fn.formatDate(this.date, this.format); + if ($.isFunction(options.pick)) { + $this.off(EVENT_PICK, options.pick); + } - if ($element.is("input")) { - $element.prop("value", date).trigger("change"); + if (this.isInput) { + $this.off(EVENT_KEYUP, this.keyup); + + if (!options.trigger) { + $this.off(EVENT_FOCUS, this.show); + } + } + + this.$trigger.off(EVENT_CLICK, this.show); + }, + + showView: function (view) { + var $yearsPicker = this.$yearsPicker; + var $monthsPicker = this.$monthsPicker; + var $daysPicker = this.$daysPicker; + var format = this.format; + + if (format.hasYear || format.hasMonth || format.hasDay) { + switch (num(view)) { + case 2: + case 'years': + $monthsPicker.addClass(CLASS_HIDE); + $daysPicker.addClass(CLASS_HIDE); + + if (format.hasYear) { + this.fillYears(); + $yearsPicker.removeClass(CLASS_HIDE); } else { - $element.text(date); + this.showView(0); } - }, - - template: function (options) { - var defaults = { - text: "", - type: "", - selected: false, - disabled: false - }; - - $.extend(defaults, options); - - return [ - '<' + this.defaults.itemTag + ' ', - (defaults.selected ? 'class="' + this.defaults.selectedClass + '"' : - defaults.disabled ? 'class="' + this.defaults.disabledClass + '"' : ''), - (defaults.type ? ' data-type="' + defaults.type + '"' : ''), - '>', - defaults.text, - '' - ].join(""); - }, - - fillAll: function () { - this.fillYears(); - this.fillMonths(); - this.fillDays(); - }, - - fillYears: function () { - var title = "", - items = [], - suffix = this.defaults.yearSuffix || "", - year = this.date.getFullYear(), - viewYear = this.viewDate.getFullYear(), - isCurrent, - i; - - title = (viewYear - 5) + suffix + " - " + (viewYear + 6) + suffix; - - for (i = -5; i < 7; i++) { - isCurrent = (viewYear + i) === year; - items.push(this.template({ - text: viewYear + i, - type: isCurrent ? "year selected" : "year", - selected: isCurrent, - disabled: i === -5 || i === 6 - })); + + break; + + case 1: + case 'months': + $yearsPicker.addClass(CLASS_HIDE); + $daysPicker.addClass(CLASS_HIDE); + + if (format.hasMonth) { + this.fillMonths(); + $monthsPicker.removeClass(CLASS_HIDE); + } else { + this.showView(2); } - this.$picker.find("[data-type='years current']").html(title); - this.$picker.find("[data-type='years']").empty().html(items.join("")); - }, - - fillMonths: function () { - var title = "", - items = [], - options = this.defaults.monthsShort, - year = this.date.getFullYear(), - month = this.date.getMonth(), - viewYear = this.viewDate.getFullYear(), - isCurrent, - i; - - title = viewYear.toString() + this.defaults.yearSuffix || ""; - - for (i = 0; i < 12; i++) { - isCurrent = viewYear === year && i === month; - - items.push(this.template({ - text: options[i], - type: isCurrent ? "month selected" : "month", - selected: isCurrent - })); + break; + + // case 0: + // case 'days': + default: + $yearsPicker.addClass(CLASS_HIDE); + $monthsPicker.addClass(CLASS_HIDE); + + if (format.hasDay) { + this.fillDays(); + $daysPicker.removeClass(CLASS_HIDE); + } else { + this.showView(1); } + } + } + }, + + hideView: function () { + if (this.options.autohide) { + this.hide(); + } + }, + + place: function () { + var options = this.options; + var $this = this.$element; + var $picker = this.$picker; + var containerWidth = $document.outerWidth(); + var containerHeight = $document.outerHeight(); + var elementWidth = $this.outerWidth(); + var elementHeight = $this.outerHeight(); + var width = $picker.width(); + var height = $picker.height(); + var offsets = $this.offset(); + var left = offsets.left; + var top = offsets.top; + var offset = parseFloat(options.offset) || 10; + var placement = CLASS_TOP_LEFT; + + if (top > height && top + elementHeight + height > containerHeight) { + top -= height + offset; + placement = CLASS_BOTTOM_LEFT; + } else { + top += elementHeight + offset; + } + + if (left + width > containerWidth) { + left = left + elementWidth - width; + placement = placement.replace('left', 'right'); + } + + $picker.removeClass(CLASS_PLACEMENTS).addClass(placement).css({ + top: top, + left: left, + zIndex: parseInt(options.zIndex, 10) + }); + }, + + // A shortcut for triggering custom events + trigger: function (type, data) { + var e = $.Event(type, data); + + this.$element.trigger(e); + + return e; + }, + + createItem: function (data) { + var options = this.options; + var itemTag = options.itemTag; + var defaults = { + text: '', + view: '', + muted: false, + picked: false, + disabled: false + }; + + $.extend(defaults, data); + + return ( + '<' + itemTag + ' ' + + (defaults.disabled ? 'class="' + options.disabledClass + '"' : + defaults.picked ? 'class="' + options.pickedClass + '"' : + defaults.muted ? 'class="' + options.mutedClass + '"' : '') + + (defaults.view ? ' data-view="' + defaults.view + '"' : '') + + '>' + + defaults.text + + '' + ); + }, + + fillAll: function () { + this.fillYears(); + this.fillMonths(); + this.fillDays(); + }, + + fillWeek: function () { + var options = this.options; + var weekStart = parseInt(options.weekStart, 10) % 7; + var days = options.daysMin; + var list = ''; + var i; + + days = $.merge(days.slice(weekStart), days.slice(0, weekStart)); + + for (i = 0; i <= 6; i++) { + list += this.createItem({ + text: days[i] + }); + } + + this.$week.html(list); + }, + + fillYears: function () { + var options = this.options; + var disabledClass = options.disabledClass || ''; + var suffix = options.yearSuffix || ''; + var filter = $.isFunction(options.filter) && options.filter; + var startDate = this.startDate; + var endDate = this.endDate; + var viewDate = this.viewDate; + var viewYear = viewDate.getFullYear(); + var viewMonth = viewDate.getMonth(); + var viewDay = viewDate.getDate(); + var date = this.date; + var year = date.getFullYear(); + var isPrevDisabled = false; + var isNextDisabled = false; + var isDisabled = false; + var isPicked = false; + var isMuted = false; + var list = ''; + var start = -5; + var end = 6; + var i; + + for (i = start; i <= end; i++) { + date = new Date(viewYear + i, viewMonth, viewDay); + isMuted = i === start || i === end; + isPicked = (viewYear + i) === year; + isDisabled = false; + + if (startDate) { + isDisabled = date.getFullYear() < startDate.getFullYear(); + + if (i === start) { + isPrevDisabled = isDisabled; + } + } - this.$picker.find("[data-type='year current']").html(title); - this.$picker.find("[data-type='months']").empty().html(items.join("")); - }, + if (!isDisabled && endDate) { + isDisabled = date.getFullYear() > endDate.getFullYear(); - fillWeek: function () { - var items = [], - options = this.defaults.daysMin, - weekStart = parseInt(this.defaults.weekStart, 10) % 7, - i; + if (i === end) { + isNextDisabled = isDisabled; + } + } - options = $.merge(options.slice(weekStart), options.slice(0, weekStart)); + if (!isDisabled && filter) { + isDisabled = filter.call(this.$element, date) === false; + } - for (i = 0; i < 7; i++) { - items.push(this.template({ - text: options[i] - })); - } + list += this.createItem({ + text: viewYear + i, + view: isDisabled ? 'year disabled' : isPicked ? 'year picked' : 'year', + muted: isMuted, + picked: isPicked, + disabled: isDisabled + }); + } + + this.$yearsPrev.toggleClass(disabledClass, isPrevDisabled); + this.$yearsNext.toggleClass(disabledClass, isNextDisabled); + this.$yearsCurrent. + toggleClass(disabledClass, true). + html((viewYear + start) + suffix + ' - ' + (viewYear + end) + suffix); + this.$years.html(list); + }, + + fillMonths: function () { + var options = this.options; + var disabledClass = options.disabledClass || ''; + var months = options.monthsShort; + var filter = $.isFunction(options.filter) && options.filter; + var startDate = this.startDate; + var endDate = this.endDate; + var viewDate = this.viewDate; + var viewYear = viewDate.getFullYear(); + var viewDay = viewDate.getDate(); + var date = this.date; + var year = date.getFullYear(); + var month = date.getMonth(); + var isPrevDisabled = false; + var isNextDisabled = false; + var isDisabled = false; + var isPicked = false; + var list = ''; + var i; + + for (i = 0; i <= 11; i++) { + date = new Date(viewYear, i, viewDay); + isPicked = viewYear === year && i === month; + isDisabled = false; + + if (startDate) { + isPrevDisabled = date.getFullYear() === startDate.getFullYear(); + isDisabled = isPrevDisabled && date.getMonth() < startDate.getMonth(); + } - this.$picker.find("[data-type='week']").empty().html(items.join("")); - }, - - fillDays: function () { - var title = "", - items = [], - prevItems = [], - currentItems = [], - nextItems = [], - options = this.defaults.monthsShort, - suffix = this.defaults.yearSuffix || "", - year = this.date.getFullYear(), - month = this.date.getMonth(), - day = this.date.getDate(), - viewYear = this.viewDate.getFullYear(), - viewMonth = this.viewDate.getMonth(), - weekStart = parseInt(this.defaults.weekStart, 10) % 7, - isCurrent, - isDisabled, - length, - date, - i, - n; - - // Title of current month - title = this.defaults.showMonthAfterYear ? (viewYear + suffix + " " + options[viewMonth]) : options[viewMonth] + " " + viewYear + suffix; - - // Days of prev month - length = viewMonth === 0 ? Datepicker.fn.getDaysInMonth(viewYear - 1, 11) : Datepicker.fn.getDaysInMonth(viewYear, viewMonth - 1); - - for (i = 1; i <= length; i++) { - prevItems.push(this.template({ - text: i, - type: "day prev", - disabled: true - })); - } + if (!isDisabled && endDate) { + isNextDisabled = date.getFullYear() === endDate.getFullYear(); + isDisabled = isNextDisabled && date.getMonth() > endDate.getMonth(); + } - date = new Date(viewYear, viewMonth, 1, 0, 0, 0, 0); // The first day of current month - n = (7 + (date.getDay() - weekStart)) % 7; - n = n > 0 ? n : 7; - prevItems = prevItems.slice((length - n)); + if (!isDisabled && filter) { + isDisabled = filter.call(this.$element, date) === false; + } - // Days of prev month next - length = viewMonth === 11 ? Datepicker.fn.getDaysInMonth(viewYear + 1, 0) : Datepicker.fn.getDaysInMonth(viewYear, viewMonth + 1); + list += this.createItem({ + index: i, + text: months[i], + view: isDisabled ? 'month disabled' : isPicked ? 'month picked' : 'month', + picked: isPicked, + disabled: isDisabled + }); + } + + this.$yearPrev.toggleClass(disabledClass, isPrevDisabled); + this.$yearNext.toggleClass(disabledClass, isNextDisabled); + this.$yearCurrent. + toggleClass(disabledClass, isPrevDisabled && isNextDisabled). + html(viewYear + options.yearSuffix || ''); + this.$months.html(list); + }, + + fillDays: function () { + var options = this.options; + var disabledClass = options.disabledClass || ''; + var suffix = options.yearSuffix || ''; + var months = options.monthsShort; + var weekStart = parseInt(options.weekStart, 10) % 7; + var filter = $.isFunction(options.filter) && options.filter; + var startDate = this.startDate; + var endDate = this.endDate; + var viewDate = this.viewDate; + var viewYear = viewDate.getFullYear(); + var viewMonth = viewDate.getMonth(); + var prevViewYear = viewYear; + var prevViewMonth = viewMonth; + var nextViewYear = viewYear; + var nextViewMonth = viewMonth; + var date = this.date; + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var isPrevDisabled = false; + var isNextDisabled = false; + var isDisabled = false; + var isPicked = false; + var prevItems = []; + var nextItems = []; + var items = []; + var total = 42; // 6 rows and 7 columns on the days picker + var length; + var i; + var n; + + // Days of previous month + // ----------------------------------------------------------------------- + + if (viewMonth === 0) { + prevViewYear -= 1; + prevViewMonth = 11; + } else { + prevViewMonth -= 1; + } + + // The length of the days of previous month + length = getDaysInMonth(prevViewYear, prevViewMonth); + + // The first day of current month + date = new Date(viewYear, viewMonth, 1); + + // The visible length of the days of previous month + // [0,1,2,3,4,5,6] - [0,1,2,3,4,5,6] => [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6] + n = date.getDay() - weekStart; + + // [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6] => [1,2,3,4,5,6,7] + if (n <= 0) { + n += 7; + } + + if (startDate) { + isPrevDisabled = date.getTime() <= startDate.getTime(); + } + + for (i = length - (n - 1); i <= length; i++) { + date = new Date(prevViewYear, prevViewMonth, i); + isDisabled = false; + + if (startDate) { + isDisabled = date.getTime() < startDate.getTime(); + } - for (i = 1; i <= length; i++) { - nextItems.push(this.template({ - text: i, - type: "day next", - disabled: true - })); - } + if (!isDisabled && filter) { + isDisabled = filter.call(this.$element, date) === false; + } - length = Datepicker.fn.getDaysInMonth(viewYear, viewMonth); - date = new Date(viewYear, viewMonth, length, 0, 0, 0, 0); // The last day of current month - n = (7 - (date.getDay() + 1 - weekStart)) % 7; - n = n >= (7 * 6 - (prevItems.length + length)) ? n : n + 7; // 7 * 6 : 7 columns & 6 rows, 42 items - nextItems = nextItems.slice(0, n); - - // Days of current month - for (i = 1; i <= length; i++) { - isCurrent = viewYear === year && viewMonth === month && i === day; - isDisabled = this.defaults.isDisabled(new Date(viewYear, viewMonth, i)); - - currentItems.push(this.template({ - text: i, - type: isDisabled ? "day disabled" : isCurrent ? "day selected" : "day", - selected: isCurrent, - disabled: isDisabled - })); - } + prevItems.push(this.createItem({ + text: i, + view: 'day prev', + muted: true, + disabled: isDisabled + })); + } - // Merge all the days - $.merge(items, prevItems); - $.merge(items, currentItems); - $.merge(items, nextItems); - - this.$picker.find("[data-type='month current']").html(title); - this.$picker.find("[data-type='days']").empty().html(items.join("")); - }, - - click: function (e) { - var $target = $(e.target), - yearRegex = /^\d{2,4}$/, - isYear = false, - viewYear, - viewMonth, - viewDay, - year, - type; - - e.stopPropagation(); - e.preventDefault(); - - if ($target.length === 0) { - return; - } + // Days of next month + // ----------------------------------------------------------------------- - viewYear = this.viewDate.getFullYear(); - viewMonth = this.viewDate.getMonth(); - viewDay = this.viewDate.getDate(); - type = $target.data().type; + if (viewMonth === 11) { + nextViewYear += 1; + nextViewYear = 0; + } else { + nextViewMonth += 1; + } - switch (type) { - case "years prev": - case "years next": - viewYear = type === "years prev" ? viewYear - 10 : viewYear + 10; - year = $target.text(); - isYear = yearRegex.test(year); - - if (isYear) { - viewYear = parseInt(year, 10); - this.date = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - } - - this.viewDate = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - this.fillYears(); - - if (isYear) { - this.showView(1); - this.output(); - } - - break; - - case "year prev": - case "year next": - viewYear = type === "year prev" ? viewYear - 1 : viewYear + 1; - this.viewDate = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - this.fillMonths(); - break; - - case "year current": - - if (this.format.year) { - this.showView(2); - } - - break; - - case "year selected": - - if (this.format.month) { - this.showView(1); - } else { - this.hideView(); - } - - break; - - case "year": - viewYear = parseInt($target.text(), 10); - this.date = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - this.viewDate = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - - if (this.format.month) { - this.showView(1); - } else { - this.hideView(); - } - - this.output(); - break; - - case "month prev": - case "month next": - viewMonth = type === "month prev" ? viewMonth - 1 : type === "month next" ? viewMonth + 1 : viewMonth; - this.viewDate = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - this.fillDays(); - break; - - case "month current": - - if (this.format.month) { - this.showView(1); - } - - break; - - case "month selected": - - if (this.format.day) { - this.showView(0); - } else { - this.hideView(); - } - - break; - - case "month": - viewMonth = $target.parent().children().index($target); - this.date = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - this.viewDate = new Date(viewYear, viewMonth, Math.min(viewDay, 28), 0, 0, 0, 0); - - if (this.format.day) { - this.showView(0); - } else { - this.hideView(); - } - - this.output(); - break; + // The length of the days of current month + length = getDaysInMonth(viewYear, viewMonth); - case "day prev": - case "day next": - case "day": - viewMonth = type === "day prev" ? viewMonth - 1 : type === "day next" ? viewMonth + 1 : viewMonth; - viewDay = parseInt($target.text(), 10); - this.date = new Date(viewYear, viewMonth, viewDay, 0, 0, 0, 0); - this.viewDate = new Date(viewYear, viewMonth, viewDay, 0, 0, 0, 0); - this.fillDays(); + // The visible length of next month + n = total - (prevItems.length + length); - if (type === "day") { - this.hideView(); - } + // The last day of current month + date = new Date(viewYear, viewMonth, length); - this.output(); - break; + if (endDate) { + isNextDisabled = date.getTime() >= endDate.getTime(); + } - case "day selected": - this.hideView(); - this.output(); - break; + for (i = 1; i <= n; i++) { + date = new Date(nextViewYear, nextViewMonth, i); + isDisabled = false; - case "day disabled": - this.hideView(); - break; + if (endDate) { + isDisabled = date.getTime() > endDate.getTime(); + } - // No default - } + if (!isDisabled && filter) { + isDisabled = filter.call(this.$element, date) === false; } - }; - // Common methods - Datepicker.fn = { - isLeapYear: function (year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - }, + nextItems.push(this.createItem({ + text: i, + view: 'day next', + muted: true, + disabled: isDisabled + })); + } - getDaysInMonth: function (year, month) { - return [31, (this.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; - }, + // Days of current month + // ----------------------------------------------------------------------- - parseFormat: function (format) { - var separator = format.match(/[.\/\-\s].*?/) || "/", - parts = format.split(/\W+/), - length, - i; + for (i = 1; i <= length; i++) { + date = new Date(viewYear, viewMonth, i); + isPicked = viewYear === year && viewMonth === month && i === day; + isDisabled = false; - if (!parts || parts.length === 0) { - throw new Error("Invalid date format."); - } + if (startDate) { + isDisabled = date.getTime() < startDate.getTime(); + } - format = { - separator: separator[0], - parts: parts - }; - - for (i = 0, length = parts.length; i < length; i++) { - switch (parts[i]) { - case "dd": - case "d": - format.day = true; - break; - - case "mm": - case "m": - format.month = true; - break; - - case "yyyy": - case "yy": - format.year = true; - break; - - // No default - } - } + if (!isDisabled && endDate) { + isDisabled = date.getTime() > endDate.getTime(); + } - return format; - }, - - parseDate: function (date, format) { - var parts, - length, - year, - day, - month, - val, - i; - - parts = typeof date === "string" && date.length > 0 ? date.split(format.separator) : []; - length = format.parts.length; - - date = new Date(); - year = date.getFullYear(); - day = date.getDate(); - month = date.getMonth(); - - if (parts.length === length) { - for (i = 0; i < length; i++) { - val = parseInt(parts[i], 10) || 1; - - switch (format.parts[i]) { - case "dd": - case "d": - day = val; - break; - - case "mm": - case "m": - month = val - 1; - break; - - case "yy": - year = 2000 + val; - break; - - case "yyyy": - year = val; - break; - - // No default - } - } - } + if (!isDisabled && filter) { + isDisabled = filter.call(this.$element, date) === false; + } - return new Date(year, month, day, 0, 0, 0, 0); - }, - - formatDate: function (date, format) { - var val = { - d: date.getDate(), - m: date.getMonth() + 1, - yy: date.getFullYear().toString().substring(2), - yyyy: date.getFullYear() - }, - parts = [], - length = format.parts.length, - i; - - val.dd = (val.d < 10 ? "0" : "") + val.d; - val.mm = (val.m < 10 ? "0" : "") + val.m; - - for (i = 0; i < length; i++) { - parts.push(val[format.parts[i]]); - } + items.push(this.createItem({ + text: i, + view: isDisabled ? 'day disabled' : isPicked ? 'day picked' : 'day', + picked: isPicked, + disabled: isDisabled + })); + } + + // Render days picker + // ----------------------------------------------------------------------- + + this.$monthPrev.toggleClass(disabledClass, isPrevDisabled); + this.$monthNext.toggleClass(disabledClass, isNextDisabled); + this.$monthCurrent. + toggleClass(disabledClass, isPrevDisabled && isNextDisabled). + html( + options.yearFirst ? + viewYear + suffix + ' ' + months[viewMonth] : + months[viewMonth] + ' ' + viewYear + suffix + ); + this.$days.html(prevItems.join('') + items.join(' ') + nextItems.join('')); + }, + + click: function (e) { + var $target = $(e.target); + var viewDate = this.viewDate; + var viewYear; + var viewMonth; + var viewDay; + var isYear; + var year; + var view; + + e.stopPropagation(); + e.preventDefault(); + + if ($target.hasClass('disabled')) { + return; + } + + viewYear = viewDate.getFullYear(); + viewMonth = viewDate.getMonth(); + viewDay = viewDate.getDate(); + view = $target.data('view'); + + switch (view) { + case 'years prev': + case 'years next': + viewYear = view === 'years prev' ? viewYear - 10 : viewYear + 10; + year = $target.text(); + isYear = REGEXP_YEAR.test(year); + + if (isYear) { + viewYear = parseInt(year, 10); + this.date = new Date(viewYear, viewMonth, min(viewDay, 28)); + } + + this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28)); + this.fillYears(); + + if (isYear) { + this.showView(1); + this.pick('year'); + } + + break; + + case 'year prev': + case 'year next': + viewYear = view === 'year prev' ? viewYear - 1 : viewYear + 1; + this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28)); + this.fillMonths(); + break; + + case 'year current': + + if (this.format.hasYear) { + this.showView(2); + } + + break; + + case 'year picked': + + if (this.format.hasMonth) { + this.showView(1); + } else { + this.hideView(); + } + + break; + + case 'year': + viewYear = parseInt($target.text(), 10); + this.date = new Date(viewYear, viewMonth, min(viewDay, 28)); + this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28)); + + if (this.format.hasMonth) { + this.showView(1); + } else { + this.hideView(); + } + + this.pick('year'); + break; + + case 'month prev': + case 'month next': + viewMonth = view === 'month prev' ? viewMonth - 1 : view === 'month next' ? viewMonth + 1 : viewMonth; + this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28)); + this.fillDays(); + break; + + case 'month current': + + if (this.format.hasMonth) { + this.showView(1); + } + + break; + + case 'month picked': + + if (this.format.hasDay) { + this.showView(0); + } else { + this.hideView(); + } + + break; + + case 'month': + viewMonth = $.inArray($target.text(), this.options.daysMin); + this.date = new Date(viewYear, viewMonth, min(viewDay, 28)); + this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28)); + + if (this.format.hasDay) { + this.showView(0); + } else { + this.hideView(); + } + + this.pick('month'); + break; + + case 'day prev': + case 'day next': + case 'day': + viewMonth = view === 'day prev' ? viewMonth - 1 : view === 'day next' ? viewMonth + 1 : viewMonth; + viewDay = parseInt($target.text(), 10); + this.date = new Date(viewYear, viewMonth, viewDay); + this.viewDate = new Date(viewYear, viewMonth, viewDay); + this.fillDays(); + + if (view === 'day') { + this.hideView(); + } + + this.pick('day'); + break; + + case 'day picked': + this.hideView(); + this.pick('day'); + break; + + // No default + } + }, + + clickDoc: function (e) { + var target = e.target; + var trigger = this.$trigger[0]; + var ignored; + + while (target !== document) { + if (target === trigger) { + ignored = true; + break; + } + + target = target.parentNode; + } - return parts.join(format.separator); + if (!ignored) { + this.hide(); + } + }, + + keyup: function () { + this.update(); + }, + + getValue: function () { + var $this = this.$element; + var val = ''; + + if (this.isInput) { + val = $this.val(); + } else if (this.isInline) { + if (this.options.container) { + val = $this.text(); } - }; + } else { + val = $this.text(); + } - Datepicker.defaults = { - autoClose: false, - dateFormat: "mm/dd/yyyy", - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], - disabledClass: "disabled", - - isDisabled: function ( /* date */ ) { - return false; - }, - - itemTag: "li", - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - selectedClass: "selected", - showMonthAfterYear: false, - template: [ - '
          ', - '
          ', - '
          ', - '
          ', - '
            ', - '
          • ', - '
          • ', - '
          • ', - '
          ', - '
            ', - '
            ', - '
            ', - '
              ', - '
            • ', - '
            • ', - '
            • ', - '
            ', - '
              ', - '
              ', - '
              ', - '
                ', - '
              • ', - '
              • ', - '
              • ', - '
              ', - '
                ', - '
                  ', - '
                  ', - '
                  ', - '
                  ' - ].join(""), - trigger: undefined, - viewStart: 0, // 0 for "days", 1 for "months", 2 for "years" - weekStart: 0, // 0 for Sunday, 1 for Monday, 2 for Tuesday, 3 for Wednesday, 4 for Thursday, 5 for Friday, 6 for Saturday - yearSuffix: "" - }; + return val; + }, - Datepicker.setDefaults = function (options) { - $.extend(Datepicker.defaults, options); - }; + setValue: function (val) { + var $this = this.$element; - // Register as jQuery plugin - $.fn.datepicker = function (options) { - return this.each(function () { - var $this = $(this), - data = $this.data("datepicker"); + val = isString(val) ? val : ''; - if (!data) { - data = new Datepicker(this, options); - $this.data("datepicker", data); - } + if (this.isInput) { + $this.val(val); + } else if (this.isInline) { + if (this.options.container) { + $this.text(val); + } + } else { + $this.text(val); + } + }, + + + // Methods + // ------------------------------------------------------------------------- + + // Show the datepicker + show: function () { + if (!this.isBuilt) { + this.build(); + } + + if (this.isShown) { + return; + } + + if (this.trigger(EVENT_SHOW).isDefaultPrevented()) { + return; + } + + this.isShown = true; + this.$picker.removeClass(CLASS_HIDE).on(EVENT_CLICK, $.proxy(this.click, this)); + this.showView(this.options.startView); + + if (!this.isInline) { + $window.on(EVENT_RESIZE, (this._place = proxy(this.place, this))); + $document.on(EVENT_CLICK, (this._clickDoc = proxy(this.clickDoc, this))); + this.place(); + } + }, + + // Hide the datepicker + hide: function () { + if (!this.isShown) { + return; + } + + if (this.trigger(EVENT_HIDE).isDefaultPrevented()) { + return; + } + + this.isShown = false; + this.$picker.addClass(CLASS_HIDE).off(EVENT_CLICK, this.click); + + if (!this.isInline) { + $window.off(EVENT_RESIZE, this._place); + $document.off(EVENT_CLICK, this._clickDoc); + } + }, + + // Update the datepicker with the current input value + update: function () { + this.setDate(this.getValue(), true); + }, + + /** + * Pick the current date to the element + * + * @param {String} _view (private) + */ + pick: function (_view) { + var $this = this.$element; + var date = this.date; + + if (this.trigger(EVENT_PICK, { + view: _view || '', + date: date + }).isDefaultPrevented()) { + return; + } + + this.setValue(date = this.formatDate(this.date)); + + if (this.isInput) { + $this.trigger('change'); + } + }, + + // Reset the datepicker + reset: function () { + this.setDate(this.initialDate, true); + this.setValue(this.initialValue); + + if (this.isShown) { + this.showView(this.options.startView); + } + }, + + /** + * Get the month name with given argument or the current date + * + * @param {Number} month (optional) + * @param {Boolean} short (optional) + * @return {String} (month name) + */ + getMonthName: function (month, short) { + var options = this.options; + var months = options.months; + + if ($.isNumeric(month)) { + month = num(month); + } else if (isUndefined(short)) { + short = month; + } + + if (short === true) { + months = options.monthsShort; + } + + return months[isNumber(month) ? month : this.date.getMonth()]; + }, + + /** + * Get the day name with given argument or the current date + * + * @param {Number} day (optional) + * @param {Boolean} short (optional) + * @param {Boolean} min (optional) + * @return {String} (day name) + */ + getDayName: function (day, short, min) { + var options = this.options; + var days = options.days; + + if ($.isNumeric(day)) { + day = num(day); + } else { + if (isUndefined(min)) { + min = short; + } - if (typeof options === "string" && $.isFunction(data[options])) { - data[options](); - } - }); - }; + if (isUndefined(short)) { + short = day; + } + } + + days = min === true ? options.daysMin : short === true ? options.daysShort : days; + + return days[isNumber(day) ? day : this.date.getDay()]; + }, + + /** + * Get the current date + * + * @param {Boolean} formated (optional) + * @return {Date|String} (date) + */ + getDate: function (formated) { + var date = this.date; + + return formated ? this.formatDate(date) : new Date(date); + }, + + /** + * Set the current date with a new date + * + * @param {Date} date + * @param {Boolean} _isUpdated (private) + */ + setDate: function (date, _isUpdated) { + var filter = this.options.filter; + + if (isDate(date) || isString(date)) { + date = this.parseDate(date); + + if ($.isFunction(filter) && filter.call(this.$element, date) === false) { + return; + } - $.fn.datepicker.constructor = Datepicker; - $.fn.datepicker.setDefaults = Datepicker.setDefaults; + this.date = date; + this.viewDate = new Date(date); - $(function () { - $("[datepicker]").datepicker(); + if (!_isUpdated) { + this.pick(); + } + + if (this.isBuilt) { + this.fillAll(); + } + } + }, + + /** + * Set the start view date with a new date + * + * @param {Date} date + */ + setStartDate: function (date) { + if (isDate(date) || isString(date)) { + this.startDate = this.parseDate(date); + + if (this.isBuilt) { + this.fillAll(); + } + } + }, + + /** + * Set the end view date with a new date + * + * @param {Date} date + */ + setEndDate: function (date) { + if (isDate(date) || isString(date)) { + this.endDate = this.parseDate(date); + + if (this.isBuilt) { + this.fillAll(); + } + } + }, + + /** + * Parse a date string with the set date format + * + * @param {String} date + * @return {Date} (parsed date) + */ + parseDate: function (date) { + var format = this.format; + var parts = []; + var length; + var year; + var day; + var month; + var val; + var i; + + if (isDate(date)) { + return new Date(date); + } else if (isString(date)) { + parts = date.match(REGEXP_DIGITS) || []; + } + + date = new Date(); + year = date.getFullYear(); + day = date.getDate(); + month = date.getMonth(); + length = format.parts.length; + + if (parts.length === length) { + for (i = 0; i < length; i++) { + val = parseInt(parts[i], 10) || 1; + + switch (format.parts[i]) { + case 'dd': + case 'd': + day = val; + break; + + case 'mm': + case 'm': + month = val - 1; + break; + + case 'yy': + year = 2000 + val; + break; + + case 'yyyy': + year = val; + break; + + // No default + } + } + } + + return new Date(year, month, day); + }, + + /** + * Format a date object to a string with the set date format + * + * @param {Date} date + * @return {String} (formated date) + */ + formatDate: function (date) { + var format = this.format; + var formated = ''; + var length; + var year; + var part; + var val; + var i; + + if (isDate(date)) { + formated = format.source; + year = date.getFullYear(); + val = { + d: date.getDate(), + m: date.getMonth() + 1, + yy: year.toString().substring(2), + yyyy: year + }; + + val.dd = (val.d < 10 ? '0' : '') + val.d; + val.mm = (val.m < 10 ? '0' : '') + val.m; + length = format.parts.length; + + for (i = 0; i < length; i++) { + part = format.parts[i]; + formated = formated.replace(part, val[part]); + } + } + + return formated; + }, + + // Destroy the datepicker and remove the instance from the target element + destroy: function () { + this.unbind(); + this.unbuild(); + this.$element.removeData(NAMESPACE); + } + }; + + Datepicker.LANGUAGES = {}; + + Datepicker.DEFAULTS = { + // Show the datepicker automatically when initialized + autoshow: false, + + // Hide the datepicker automatically when picked + autohide: false, + + // Pick the initial date automatically when initialized + autopick: false, + + // Enable inline mode + inline: false, + + // A element (or selector) for putting the datepicker + container: null, + + // A element (or selector) for triggering the datepicker + trigger: null, + + // The ISO language code (built-in: en-US) + language: '', + + // The date string format + format: 'mm/dd/yyyy', + + // The initial date + date: null, + + // The start view date + startDate: null, + + // The end view date + endDate: null, + + // The start view when initialized + startView: 0, // 0 for days, 1 for months, 2 for years + + // The start day of the week + weekStart: 0, // 0 for Sunday, 1 for Monday, 2 for Tuesday, 3 for Wednesday, 4 for Thursday, 5 for Friday, 6 for Saturday + + // Show year before month on the datepicker header + yearFirst: false, + + // A string suffix to the year number. + yearSuffix: '', + + // Days' name of the week. + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + + // Shorter days' name + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + + // Shortest days' name + daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + + // Months' name + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + + // Shorter months' name + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + + // A element tag for each item of years, months and days + itemTag: 'li', + + // A class (CSS) for muted date item + mutedClass: 'muted', + + // A class (CSS) for picked date item + pickedClass: 'picked', + + // A class (CSS) for disabled date item + disabledClass: 'disabled', + + // The template of the datepicker + template: ( + '
                  ' + + '
                  ' + + '' + + '' + + '
                  ' + + '
                  ' + + '' + + '' + + '
                  ' + + '
                  ' + + '' + + '' + + '' + + '
                  ' + + '
                  ' + ), + + // The offset top or bottom of the datepicker from the element + offset: 10, + + // The `z-index` of the datepicker + zIndex: 1, + + // Filter each date item (return `false` to disable a date item) + filter: null, + + // Event shortcuts + show: null, + hide: null, + pick: null + }; + + Datepicker.setDefaults = function (options) { + $.extend(Datepicker.DEFAULTS, $.isPlainObject(options) && options); + }; + + // Save the other datepicker + Datepicker.other = $.fn.datepicker; + + // Register as jQuery plugin + $.fn.datepicker = function (option) { + var args = toArray(arguments, 1); + var result; + + this.each(function () { + var $this = $(this); + var data = $this.data(NAMESPACE); + var options; + var fn; + + if (!data) { + if (/destroy/.test(option)) { + return; + } + + options = $.extend({}, $this.data(), $.isPlainObject(option) && option); + $this.data(NAMESPACE, (data = new Datepicker(this, options))); + } + + if (isString(option) && $.isFunction(fn = data[option])) { + result = fn.apply(data, args); + } }); + + return isUndefined(result) ? this : result; + }; + + $.fn.datepicker.Constructor = Datepicker; + $.fn.datepicker.languages = Datepicker.LANGUAGES; + $.fn.datepicker.setDefaults = Datepicker.setDefaults; + + // No conflict + $.fn.datepicker.noConflict = function () { + $.fn.datepicker = Datepicker.other; + return this; + }; + }); diff --git a/dist/datepicker.min.css b/dist/datepicker.min.css index 899b496..f800dc5 100644 --- a/dist/datepicker.min.css +++ b/dist/datepicker.min.css @@ -1,9 +1,9 @@ /*! - * Datepicker v0.1.0 + * Datepicker v0.2.0 * https://github.com/fengyuanchen/datepicker * - * Copyright 2014 Fengyuan Chen + * Copyright (c) 2014-2015 Fengyuan Chen * Released under the MIT license - */ - -.datepicker-container{font-size:12px;font-family:arial,helvetica,sans-serif;line-height:30px;float:left;position:absolute;z-index:201312}.datepicker-arrow,.datepicker-arrow:after{border:5px solid transparent;display:block;height:0;width:0}.datepicker-arrow{border-bottom-color:#9cf;margin:0 10px;position:relative}.datepicker-arrow:after{border-bottom-color:#fff;content:" ";left:-5px;position:absolute;top:-4px}.datepicker-content{border:1px solid #ccc;border-top-color:#9cf;box-shadow:0 0 3px #ccc;width:210px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.datepicker-next,.datepicker-prev{font-size:18px}.datepicker-content ul:after,.datepicker-content ul:before{content:"";display:table}.datepicker-content ul:after{clear:both}.datepicker-content ul{margin:0;padding:0;width:102%}.datepicker-content li{background-color:#fff;cursor:pointer;float:left;height:30px;list-style:none;margin:0;padding:0;text-align:center;width:30px}.datepicker-content .col-1{width:30px}.datepicker-content .col-2{width:60px}.datepicker-content .col-3{width:90px}.datepicker-content .col-4{width:120px}.datepicker-content .col-5{width:150px}.datepicker-content .col-6{width:180px}.datepicker-content .col-7{width:210px}.datepicker-content li:hover{background-color:#eee}.datepicker-content .selected,.datepicker-content .selected:hover{color:#36f}.datepicker-content .disabled,.datepicker-content .disabled:hover{color:#ccc}.datepicker-months li,.datepicker-years li{height:52.5px;line-height:52.5px;width:52.5px}.datepicker-week li,.datepicker-week li:hover{background-color:#fff;cursor:default} \ No newline at end of file + * + * Date: 2015-10-18T06:04:18.882Z + */.datepicker-container{position:fixed;top:0;left:0;z-index:-1;width:210px;font-size:12px;line-height:30px;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#fff;direction:ltr!important;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.datepicker-container:after,.datepicker-container:before{position:absolute;display:block;width:0;height:0;content:" ";border:5px solid transparent}.datepicker-dropdown{position:absolute;z-index:1;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:1px solid #ccc;-webkit-box-shadow:0 3px 6px #ccc;box-shadow:0 3px 6px #ccc}.datepicker-inline{position:static}.datepicker-top-left,.datepicker-top-right{border-top-color:#39f}.datepicker-top-left:after,.datepicker-top-left:before,.datepicker-top-right:after,.datepicker-top-right:before{top:-5px;left:10px;border-top:0}.datepicker-top-left:before,.datepicker-top-right:before{border-bottom-color:#39f}.datepicker-top-left:after,.datepicker-top-right:after{top:-4px;border-bottom-color:#fff}.datepicker-bottom-left,.datepicker-bottom-right{border-bottom-color:#39f}.datepicker-bottom-left:after,.datepicker-bottom-left:before,.datepicker-bottom-right:after,.datepicker-bottom-right:before{bottom:-5px;left:10px;border-bottom:0}.datepicker-bottom-left:before,.datepicker-bottom-right:before{border-top-color:#39f}.datepicker-bottom-left:after,.datepicker-bottom-right:after{bottom:-4px;border-top-color:#fff}.datepicker-bottom-right:after,.datepicker-bottom-right:before,.datepicker-top-right:after,.datepicker-top-right:before{right:10px;left:auto}.datepicker-panel>ul:after,.datepicker-panel>ul:before{display:table;content:" "}.datepicker-panel>ul:after{clear:both}.datepicker-panel>ul{width:102%;padding:0;margin:0}.datepicker-panel>ul>li{float:left;width:30px;height:30px;padding:0;margin:0;text-align:center;list-style:none;cursor:pointer;background-color:#fff}.datepicker-panel>ul>li:hover{background-color:#eee}.datepicker-panel>ul>li.muted,.datepicker-panel>ul>li.muted:hover{color:#999}.datepicker-panel>ul>li.picked,.datepicker-panel>ul>li.picked:hover{color:#39f}.datepicker-panel>ul>li.disabled,.datepicker-panel>ul>li.disabled:hover{color:#ccc;cursor:default;background-color:#fff}.datepicker-panel>ul>li[data-view="years prev"],.datepicker-panel>ul>li[data-view="year prev"],.datepicker-panel>ul>li[data-view="month prev"],.datepicker-panel>ul>li[data-view="years next"],.datepicker-panel>ul>li[data-view="year next"],.datepicker-panel>ul>li[data-view="month next"],.datepicker-panel>ul>li[data-view=next]{font-size:18px}.datepicker-panel>ul>li[data-view="month current"],.datepicker-panel>ul>li[data-view="years current"],.datepicker-panel>ul>li[data-view="year current"]{width:150px}.datepicker-panel>ul[data-view=years]>li,.datepicker-panel>ul[data-view=months]>li{width:52.5px;height:52.5px;line-height:52.5px}.datepicker-panel>ul[data-view=week]>li,.datepicker-panel>ul[data-view=week]>li:hover{cursor:default;background-color:#fff}.datepicker-hide{display:none} \ No newline at end of file diff --git a/dist/datepicker.min.js b/dist/datepicker.min.js index b0c7815..6799540 100644 --- a/dist/datepicker.min.js +++ b/dist/datepicker.min.js @@ -1,9 +1,10 @@ /*! - * Datepicker v0.1.0 + * Datepicker v0.2.0 * https://github.com/fengyuanchen/datepicker * - * Copyright 2014 Fengyuan Chen + * Copyright (c) 2014-2015 Fengyuan Chen * Released under the MIT license + * + * Date: 2015-10-18T06:04:17.960Z */ - -!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){"use strict";var b=a(window),c=a(document),d=function(b,c){this.$element=a(b),this.defaults=a.extend({},d.defaults,this.$element.data(),a.isPlainObject(c)?c:{}),this.init()};d.prototype={constructor:d,init:function(){var b=this.defaults.trigger;this.$trigger=b?a(b):this.$element,this.$picker=a(this.defaults.template),this.$years=this.$picker.find("[data-type='years picker']"),this.$months=this.$picker.find("[data-type='months picker']"),this.$days=this.$picker.find("[data-type='days picker']"),this.$picker.appendTo("body"),this.place(),this.hide(),this.format=d.fn.parseFormat(this.defaults.dateFormat),this.fillWeek(),this.enable()},enable:function(){this.enabled||(this.$element.is("input")&&(this.$element.on("keyup",a.proxy(this.update,this)),this.defaults.trigger||this.$element.on("focus",a.proxy(this.show,this))),this.$trigger.on("click",a.proxy(this.show,this)),this.$picker.on({click:a.proxy(this.click,this),mousedown:a.proxy(this.mousedown,this)}),this.update(),this.enabled=!0)},disable:function(){this.enabled&&(this.$element.is("input")&&(this.$element.off("keyup",this.update),this.defaults.trigger||this.$element.off("focus",this.show)),this.$trigger.off("click",this.show),this.$picker.off({click:this.click,mousedown:this.mousedown}),this.hide(),this.enabled=!1)},showView:function(a){var b=this.format;if(b.year||b.month||b.day)switch(a){case 2:case"years":this.$months.hide(),this.$days.hide(),b.year?(this.fillYears(),this.$years.show()):this.showView(0);break;case 1:case"months":this.$years.hide(),this.$days.hide(),b.month?(this.fillMonths(),this.$months.show()):this.showView(2);break;default:this.$years.hide(),this.$months.hide(),b.day?(this.fillDays(),this.$days.show()):this.showView(1)}},hideView:function(){this.defaults.autoClose&&this.hide()},place:function(){var a=this.$trigger.offset(),b=this.$trigger.outerHeight();this.$picker.css({top:a.top+b,left:a.left})},show:function(){this.enabled&&(this.$picker.show(),b.on("resize",a.proxy(this.place,this)),c.on("mousedown",a.proxy(this.hide,this)),this.place(),this.showView(this.defaults.viewStart))},hide:function(){this.$picker.hide(),b.off("resize",this.place),c.off("mousedown",this.hide)},mousedown:function(a){a.stopPropagation(),a.preventDefault()},update:function(){var a=this.$element.is("input")?this.$element.prop("value"):this.$element.text();this.date=d.fn.parseDate(a,this.format),this.viewDate=new Date(this.date.getFullYear(),this.date.getMonth(),this.date.getDate(),0,0,0,0),this.fillAll()},output:function(){var a=this.$element,b=d.fn.formatDate(this.date,this.format);a.is("input")?a.prop("value",b).trigger("change"):a.text(b)},template:function(b){var c={text:"",type:"",selected:!1,disabled:!1};return a.extend(c,b),["<"+this.defaults.itemTag+" ",c.selected?'class="'+this.defaults.selectedClass+'"':c.disabled?'class="'+this.defaults.disabledClass+'"':"",c.type?' data-type="'+c.type+'"':"",">",c.text,""].join("")},fillAll:function(){this.fillYears(),this.fillMonths(),this.fillDays()},fillYears:function(){var a,b,c="",d=[],e=this.defaults.yearSuffix||"",f=this.date.getFullYear(),g=this.viewDate.getFullYear();for(c=g-5+e+" - "+(g+6)+e,b=-5;7>b;b++)a=g+b===f,d.push(this.template({text:g+b,type:a?"year selected":"year",selected:a,disabled:-5===b||6===b}));this.$picker.find("[data-type='years current']").html(c),this.$picker.find("[data-type='years']").empty().html(d.join(""))},fillMonths:function(){var a,b,c="",d=[],e=this.defaults.monthsShort,f=this.date.getFullYear(),g=this.date.getMonth(),h=this.viewDate.getFullYear();for(c=h.toString()+this.defaults.yearSuffix||"",b=0;12>b;b++)a=h===f&&b===g,d.push(this.template({text:e[b],type:a?"month selected":"month",selected:a}));this.$picker.find("[data-type='year current']").html(c),this.$picker.find("[data-type='months']").empty().html(d.join(""))},fillWeek:function(){var b,c=[],d=this.defaults.daysMin,e=parseInt(this.defaults.weekStart,10)%7;for(d=a.merge(d.slice(e),d.slice(0,e)),b=0;7>b;b++)c.push(this.template({text:d[b]}));this.$picker.find("[data-type='week']").empty().html(c.join(""))},fillDays:function(){var b,c,e,f,g,h,i="",j=[],k=[],l=[],m=[],n=this.defaults.monthsShort,o=this.defaults.yearSuffix||"",p=this.date.getFullYear(),q=this.date.getMonth(),r=this.date.getDate(),s=this.viewDate.getFullYear(),t=this.viewDate.getMonth(),u=parseInt(this.defaults.weekStart,10)%7;for(i=this.defaults.showMonthAfterYear?s+o+" "+n[t]:n[t]+" "+s+o,e=0===t?d.fn.getDaysInMonth(s-1,11):d.fn.getDaysInMonth(s,t-1),g=1;e>=g;g++)k.push(this.template({text:g,type:"day prev",disabled:!0}));for(f=new Date(s,t,1,0,0,0,0),h=(7+(f.getDay()-u))%7,h=h>0?h:7,k=k.slice(e-h),e=11===t?d.fn.getDaysInMonth(s+1,0):d.fn.getDaysInMonth(s,t+1),g=1;e>=g;g++)m.push(this.template({text:g,type:"day next",disabled:!0}));for(e=d.fn.getDaysInMonth(s,t),f=new Date(s,t,e,0,0,0,0),h=(7-(f.getDay()+1-u))%7,h=h>=42-(k.length+e)?h:h+7,m=m.slice(0,h),g=1;e>=g;g++)b=s===p&&t===q&&g===r,c=this.defaults.isDisabled(new Date(s,t,g)),l.push(this.template({text:g,type:c?"day disabled":b?"day selected":"day",selected:b,disabled:c}));a.merge(j,k),a.merge(j,l),a.merge(j,m),this.$picker.find("[data-type='month current']").html(i),this.$picker.find("[data-type='days']").empty().html(j.join(""))},click:function(b){var c,d,e,f,g,h=a(b.target),i=/^\d{2,4}$/,j=!1;if(b.stopPropagation(),b.preventDefault(),0!==h.length)switch(c=this.viewDate.getFullYear(),d=this.viewDate.getMonth(),e=this.viewDate.getDate(),g=h.data().type){case"years prev":case"years next":c="years prev"===g?c-10:c+10,f=h.text(),j=i.test(f),j&&(c=parseInt(f,10),this.date=new Date(c,d,Math.min(e,28),0,0,0,0)),this.viewDate=new Date(c,d,Math.min(e,28),0,0,0,0),this.fillYears(),j&&(this.showView(1),this.output());break;case"year prev":case"year next":c="year prev"===g?c-1:c+1,this.viewDate=new Date(c,d,Math.min(e,28),0,0,0,0),this.fillMonths();break;case"year current":this.format.year&&this.showView(2);break;case"year selected":this.format.month?this.showView(1):this.hideView();break;case"year":c=parseInt(h.text(),10),this.date=new Date(c,d,Math.min(e,28),0,0,0,0),this.viewDate=new Date(c,d,Math.min(e,28),0,0,0,0),this.format.month?this.showView(1):this.hideView(),this.output();break;case"month prev":case"month next":d="month prev"===g?d-1:"month next"===g?d+1:d,this.viewDate=new Date(c,d,Math.min(e,28),0,0,0,0),this.fillDays();break;case"month current":this.format.month&&this.showView(1);break;case"month selected":this.format.day?this.showView(0):this.hideView();break;case"month":d=h.parent().children().index(h),this.date=new Date(c,d,Math.min(e,28),0,0,0,0),this.viewDate=new Date(c,d,Math.min(e,28),0,0,0,0),this.format.day?this.showView(0):this.hideView(),this.output();break;case"day prev":case"day next":case"day":d="day prev"===g?d-1:"day next"===g?d+1:d,e=parseInt(h.text(),10),this.date=new Date(c,d,e,0,0,0,0),this.viewDate=new Date(c,d,e,0,0,0,0),this.fillDays(),"day"===g&&this.hideView(),this.output();break;case"day selected":this.hideView(),this.output();break;case"day disabled":this.hideView()}}},d.fn={isLeapYear:function(a){return a%4===0&&a%100!==0||a%400===0},getDaysInMonth:function(a,b){return[31,this.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]},parseFormat:function(a){var b,c,d=a.match(/[.\/\-\s].*?/)||"/",e=a.split(/\W+/);if(!e||0===e.length)throw new Error("Invalid date format.");for(a={separator:d[0],parts:e},c=0,b=e.length;b>c;c++)switch(e[c]){case"dd":case"d":a.day=!0;break;case"mm":case"m":a.month=!0;break;case"yyyy":case"yy":a.year=!0}return a},parseDate:function(a,b){var c,d,e,f,g,h,i;if(c="string"==typeof a&&a.length>0?a.split(b.separator):[],d=b.parts.length,a=new Date,e=a.getFullYear(),f=a.getDate(),g=a.getMonth(),c.length===d)for(i=0;d>i;i++)switch(h=parseInt(c[i],10)||1,b.parts[i]){case"dd":case"d":f=h;break;case"mm":case"m":g=h-1;break;case"yy":e=2e3+h;break;case"yyyy":e=h}return new Date(e,g,f,0,0,0,0)},formatDate:function(a,b){var c,d={d:a.getDate(),m:a.getMonth()+1,yy:a.getFullYear().toString().substring(2),yyyy:a.getFullYear()},e=[],f=b.parts.length;for(d.dd=(d.d<10?"0":"")+d.d,d.mm=(d.m<10?"0":"")+d.m,c=0;f>c;c++)e.push(d[b.parts[c]]);return e.join(b.separator)}},d.defaults={autoClose:!1,dateFormat:"mm/dd/yyyy",days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],disabledClass:"disabled",isDisabled:function(){return!1},itemTag:"li",months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],selectedClass:"selected",showMonthAfterYear:!1,template:['
                  ','
                  ','
                  ','
                  ','
                    ','
                  • ','
                  • ','
                  • ',"
                  ",'
                    ',"
                    ",'
                    ','
                      ','
                    • ','
                    • ','
                    • ',"
                    ",'
                      ',"
                      ",'
                      ','
                        ','
                      • ','
                      • ','
                      • ',"
                      ",'
                        ','
                          ',"
                          ","
                          ","
                          "].join(""),trigger:void 0,viewStart:0,weekStart:0,yearSuffix:""},d.setDefaults=function(b){a.extend(d.defaults,b)},a.fn.datepicker=function(b){return this.each(function(){var c=a(this),e=c.data("datepicker");e||(e=new d(this,b),c.data("datepicker",e)),"string"==typeof b&&a.isFunction(e[b])&&e[b]()})},a.fn.datepicker.constructor=d,a.fn.datepicker.setDefaults=d.setDefaults,a(function(){a("[datepicker]").datepicker()})}); \ No newline at end of file +!function(t){"function"==typeof define&&define.amd?define("datepicker",["jquery"],t):t("object"==typeof exports?require("jquery"):jQuery)}(function(t){"use strict";function e(t){return"string"==typeof t}function i(t){return"number"==typeof t&&!isNaN(t)}function s(t){return"undefined"==typeof t}function a(t){return"object"!=typeof t||null===t?!1:"[object Date]"===A.call(t)}function n(t,e){var s=[];return i(e)&&s.push(e),s.slice.apply(t,s)}function r(t,e){var i=n(arguments,2);return function(){return t.apply(e,i.concat(n(arguments)))}}function h(t){return t%4===0&&t%100!==0||t%400===0}function o(t,e){return[31,h(t)?29:28,31,30,31,30,31,31,30,31,30,31][e]}function l(t){var e,i,s=String(t).toLowerCase(),a=s.match(b);if(!a||0===a.length)throw new Error("Invalid date format.");for(t={source:s,parts:a},e=a.length,i=0;e>i;i++)switch(a[i]){case"dd":case"d":t.hasDay=!0;break;case"mm":case"m":t.hasMonth=!0;break;case"yyyy":case"yy":t.hasYear=!0}return t}function d(e,i){this.$element=t(e),this.options=t.extend({},d.DEFAULTS,t.isPlainObject(i)&&i),this.isBuilt=!1,this.isShown=!1,this.isInput=!1,this.isInline=!1,this.initialValue="",this.initialDate=null,this.startDate=null,this.endDate=null,this.init()}var u=t(window),c=window.document,f=t(c),p="datepicker",w="click."+p,y="keyup."+p,m="focus."+p,g="resize."+p,v="show."+p,D="hide."+p,k="pick."+p,b=/y+|m+|d+/g,$=/\d+/g,x=/^\d{2,4}$/,C="datepicker-inline",F="datepicker-dropdown",S="datepicker-top-left",M="datepicker-top-right",I="datepicker-bottom-left",V="datepicker-bottom-right",T=[S,M,I,V].join(" "),Y="datepicker-hide",N=Number,P=Math.min,A=Object.prototype.toString;d.prototype={constructor:d,version:"0.2.0",init:function(){var e=this.options,i=this.$element,s=e.startDate,a=e.endDate,n=e.date;e.language&&t.extend(e,d.LANGUAGES[e.language]),this.$trigger=t(e.trigger||i),this.isInput=i.is("input")||i.is("textarea"),this.isInline=e.inline&&(e.container||!this.isInput),this.format=l(e.format),this.initialValue=this.getValue(),n=this.parseDate(n||this.initialValue),s&&(s=this.parseDate(s),n.getTime()a.getTime()&&(n=new Date(a)),this.endDate=a),this.date=n,this.viewDate=new Date(n),this.initialDate=new Date(this.date),this.bind(),(e.autoshow||this.isInline)&&this.show(),e.autopick&&this.pick()},build:function(){var e,i=this.options,s=this.$element;this.isBuilt||(this.isBuilt=!0,this.$picker=e=t(i.template),this.$week=e.find('[data-view="week"]'),this.$yearsPicker=e.find('[data-view="years picker"]'),this.$yearsPrev=e.find('[data-view="years prev"]'),this.$yearsNext=e.find('[data-view="years next"]'),this.$yearsCurrent=e.find('[data-view="years current"]'),this.$years=e.find('[data-view="years"]'),this.$monthsPicker=e.find('[data-view="months picker"]'),this.$yearPrev=e.find('[data-view="year prev"]'),this.$yearNext=e.find('[data-view="year next"]'),this.$yearCurrent=e.find('[data-view="year current"]'),this.$months=e.find('[data-view="months"]'),this.$daysPicker=e.find('[data-view="days picker"]'),this.$monthPrev=e.find('[data-view="month prev"]'),this.$monthNext=e.find('[data-view="month next"]'),this.$monthCurrent=e.find('[data-view="month current"]'),this.$days=e.find('[data-view="days"]'),this.isInline?t(i.container||s).append(e.addClass(C)):(t(c.body).append(e.addClass(F)),e.addClass(Y)),this.fillWeek())},unbuild:function(){this.isBuilt&&(this.isBuilt=!1,this.$picker.remove())},bind:function(){var e=this.options,i=this.$element;t.isFunction(e.show)&&i.on(v,e.show),t.isFunction(e.hide)&&i.on(D,e.hide),t.isFunction(e.pick)&&i.on(k,e.pick),this.isInput&&(i.on(y,t.proxy(this.keyup,this)),e.trigger||i.on(m,t.proxy(this.show,this))),this.$trigger.on(w,t.proxy(this.show,this))},unbind:function(){var e=this.options,i=this.$element;t.isFunction(e.show)&&i.off(v,e.show),t.isFunction(e.hide)&&i.off(D,e.hide),t.isFunction(e.pick)&&i.off(k,e.pick),this.isInput&&(i.off(y,this.keyup),e.trigger||i.off(m,this.show)),this.$trigger.off(w,this.show)},showView:function(t){var e=this.$yearsPicker,i=this.$monthsPicker,s=this.$daysPicker,a=this.format;if(a.hasYear||a.hasMonth||a.hasDay)switch(N(t)){case 2:case"years":i.addClass(Y),s.addClass(Y),a.hasYear?(this.fillYears(),e.removeClass(Y)):this.showView(0);break;case 1:case"months":e.addClass(Y),s.addClass(Y),a.hasMonth?(this.fillMonths(),i.removeClass(Y)):this.showView(2);break;default:e.addClass(Y),i.addClass(Y),a.hasDay?(this.fillDays(),s.removeClass(Y)):this.showView(1)}},hideView:function(){this.options.autohide&&this.hide()},place:function(){var t=this.options,e=this.$element,i=this.$picker,s=f.outerWidth(),a=f.outerHeight(),n=e.outerWidth(),r=e.outerHeight(),h=i.width(),o=i.height(),l=e.offset(),d=l.left,u=l.top,c=parseFloat(t.offset)||10,p=S;u>o&&u+r+o>a?(u-=o+c,p=I):u+=r+c,d+h>s&&(d=d+n-h,p=p.replace("left","right")),i.removeClass(T).addClass(p).css({top:u,left:d,zIndex:parseInt(t.zIndex,10)})},trigger:function(e,i){var s=t.Event(e,i);return this.$element.trigger(s),s},createItem:function(e){var i=this.options,s=i.itemTag,a={text:"",view:"",muted:!1,picked:!1,disabled:!1};return t.extend(a,e),"<"+s+" "+(a.disabled?'class="'+i.disabledClass+'"':a.picked?'class="'+i.pickedClass+'"':a.muted?'class="'+i.mutedClass+'"':"")+(a.view?' data-view="'+a.view+'"':"")+">"+a.text+""},fillAll:function(){this.fillYears(),this.fillMonths(),this.fillDays()},fillWeek:function(){var e,i=this.options,s=parseInt(i.weekStart,10)%7,a=i.daysMin,n="";for(a=t.merge(a.slice(s),a.slice(0,s)),e=0;6>=e;e++)n+=this.createItem({text:a[e]});this.$week.html(n)},fillYears:function(){var e,i=this.options,s=i.disabledClass||"",a=i.yearSuffix||"",n=t.isFunction(i.filter)&&i.filter,r=this.startDate,h=this.endDate,o=this.viewDate,l=o.getFullYear(),d=o.getMonth(),u=o.getDate(),c=this.date,f=c.getFullYear(),p=!1,w=!1,y=!1,m=!1,g=!1,v="",D=-5,k=6;for(e=D;k>=e;e++)c=new Date(l+e,d,u),g=e===D||e===k,m=l+e===f,y=!1,r&&(y=c.getFullYear()h.getFullYear(),e===k&&(w=y)),!y&&n&&(y=n.call(this.$element,c)===!1),v+=this.createItem({text:l+e,view:y?"year disabled":m?"year picked":"year",muted:g,picked:m,disabled:y});this.$yearsPrev.toggleClass(s,p),this.$yearsNext.toggleClass(s,w),this.$yearsCurrent.toggleClass(s,!0).html(l+D+a+" - "+(l+k)+a),this.$years.html(v)},fillMonths:function(){var e,i=this.options,s=i.disabledClass||"",a=i.monthsShort,n=t.isFunction(i.filter)&&i.filter,r=this.startDate,h=this.endDate,o=this.viewDate,l=o.getFullYear(),d=o.getDate(),u=this.date,c=u.getFullYear(),f=u.getMonth(),p=!1,w=!1,y=!1,m=!1,g="";for(e=0;11>=e;e++)u=new Date(l,e,d),m=l===c&&e===f,y=!1,r&&(p=u.getFullYear()===r.getFullYear(),y=p&&u.getMonth()h.getMonth()),!y&&n&&(y=n.call(this.$element,u)===!1),g+=this.createItem({index:e,text:a[e],view:y?"month disabled":m?"month picked":"month",picked:m,disabled:y});this.$yearPrev.toggleClass(s,p),this.$yearNext.toggleClass(s,w),this.$yearCurrent.toggleClass(s,p&&w).html(l+i.yearSuffix||""),this.$months.html(g)},fillDays:function(){var e,i,s,a=this.options,n=a.disabledClass||"",r=a.yearSuffix||"",h=a.monthsShort,l=parseInt(a.weekStart,10)%7,d=t.isFunction(a.filter)&&a.filter,u=this.startDate,c=this.endDate,f=this.viewDate,p=f.getFullYear(),w=f.getMonth(),y=p,m=w,g=p,v=w,D=this.date,k=D.getFullYear(),b=D.getMonth(),$=D.getDate(),x=!1,C=!1,F=!1,S=!1,M=[],I=[],V=[],T=42;for(0===w?(y-=1,m=11):m-=1,e=o(y,m),D=new Date(p,w,1),s=D.getDay()-l,0>=s&&(s+=7),u&&(x=D.getTime()<=u.getTime()),i=e-(s-1);e>=i;i++)D=new Date(y,m,i),F=!1,u&&(F=D.getTime()=c.getTime()),i=1;s>=i;i++)D=new Date(g,v,i),F=!1,c&&(F=D.getTime()>c.getTime()),!F&&d&&(F=d.call(this.$element,D)===!1),I.push(this.createItem({text:i,view:"day next",muted:!0,disabled:F}));for(i=1;e>=i;i++)D=new Date(p,w,i),S=p===k&&w===b&&i===$,F=!1,u&&(F=D.getTime()c.getTime()),!F&&d&&(F=d.call(this.$element,D)===!1),V.push(this.createItem({text:i,view:F?"day disabled":S?"day picked":"day",picked:S,disabled:F}));this.$monthPrev.toggleClass(n,x),this.$monthNext.toggleClass(n,C),this.$monthCurrent.toggleClass(n,x&&C).html(a.yearFirst?p+r+" "+h[w]:h[w]+" "+p+r),this.$days.html(M.join("")+V.join(" ")+I.join(""))},click:function(e){var i,s,a,n,r,h,o=t(e.target),l=this.viewDate;if(e.stopPropagation(),e.preventDefault(),!o.hasClass("disabled"))switch(i=l.getFullYear(),s=l.getMonth(),a=l.getDate(),h=o.data("view")){case"years prev":case"years next":i="years prev"===h?i-10:i+10,r=o.text(),n=x.test(r),n&&(i=parseInt(r,10),this.date=new Date(i,s,P(a,28))),this.viewDate=new Date(i,s,P(a,28)),this.fillYears(),n&&(this.showView(1),this.pick("year"));break;case"year prev":case"year next":i="year prev"===h?i-1:i+1,this.viewDate=new Date(i,s,P(a,28)),this.fillMonths();break;case"year current":this.format.hasYear&&this.showView(2);break;case"year picked":this.format.hasMonth?this.showView(1):this.hideView();break;case"year":i=parseInt(o.text(),10),this.date=new Date(i,s,P(a,28)),this.viewDate=new Date(i,s,P(a,28)),this.format.hasMonth?this.showView(1):this.hideView(),this.pick("year");break;case"month prev":case"month next":s="month prev"===h?s-1:"month next"===h?s+1:s,this.viewDate=new Date(i,s,P(a,28)),this.fillDays();break;case"month current":this.format.hasMonth&&this.showView(1);break;case"month picked":this.format.hasDay?this.showView(0):this.hideView();break;case"month":s=t.inArray(o.text(),this.options.daysMin),this.date=new Date(i,s,P(a,28)),this.viewDate=new Date(i,s,P(a,28)),this.format.hasDay?this.showView(0):this.hideView(),this.pick("month");break;case"day prev":case"day next":case"day":s="day prev"===h?s-1:"day next"===h?s+1:s,a=parseInt(o.text(),10),this.date=new Date(i,s,a),this.viewDate=new Date(i,s,a),this.fillDays(),"day"===h&&this.hideView(),this.pick("day");break;case"day picked":this.hideView(),this.pick("day")}},clickDoc:function(t){for(var e,i=t.target,s=this.$trigger[0];i!==c;){if(i===s){e=!0;break}i=i.parentNode}e||this.hide()},keyup:function(){this.update()},getValue:function(){var t=this.$element,e="";return this.isInput?e=t.val():this.isInline?this.options.container&&(e=t.text()):e=t.text(),e},setValue:function(t){var i=this.$element;t=e(t)?t:"",this.isInput?i.val(t):this.isInline?this.options.container&&i.text(t):i.text(t)},show:function(){this.isBuilt||this.build(),this.isShown||this.trigger(v).isDefaultPrevented()||(this.isShown=!0,this.$picker.removeClass(Y).on(w,t.proxy(this.click,this)),this.showView(this.options.startView),this.isInline||(u.on(g,this._place=r(this.place,this)),f.on(w,this._clickDoc=r(this.clickDoc,this)),this.place()))},hide:function(){this.isShown&&(this.trigger(D).isDefaultPrevented()||(this.isShown=!1,this.$picker.addClass(Y).off(w,this.click),this.isInline||(u.off(g,this._place),f.off(w,this._clickDoc))))},update:function(){this.setDate(this.getValue(),!0)},pick:function(t){var e=this.$element,i=this.date;this.trigger(k,{view:t||"",date:i}).isDefaultPrevented()||(this.setValue(i=this.formatDate(this.date)),this.isInput&&e.trigger("change"))},reset:function(){this.setDate(this.initialDate,!0),this.setValue(this.initialValue),this.isShown&&this.showView(this.options.startView)},getMonthName:function(e,a){var n=this.options,r=n.months;return t.isNumeric(e)?e=N(e):s(a)&&(a=e),a===!0&&(r=n.monthsShort),r[i(e)?e:this.date.getMonth()]},getDayName:function(e,a,n){var r=this.options,h=r.days;return t.isNumeric(e)?e=N(e):(s(n)&&(n=a),s(a)&&(a=e)),h=n===!0?r.daysMin:a===!0?r.daysShort:h,h[i(e)?e:this.date.getDay()]},getDate:function(t){var e=this.date;return t?this.formatDate(e):new Date(e)},setDate:function(i,s){var n=this.options.filter;if(a(i)||e(i)){if(i=this.parseDate(i),t.isFunction(n)&&n.call(this.$element,i)===!1)return;this.date=i,this.viewDate=new Date(i),s||this.pick(),this.isBuilt&&this.fillAll()}},setStartDate:function(t){(a(t)||e(t))&&(this.startDate=this.parseDate(t),this.isBuilt&&this.fillAll())},setEndDate:function(t){(a(t)||e(t))&&(this.endDate=this.parseDate(t),this.isBuilt&&this.fillAll())},parseDate:function(t){var i,s,n,r,h,o,l=this.format,d=[];if(a(t))return new Date(t);if(e(t)&&(d=t.match($)||[]),t=new Date,s=t.getFullYear(),n=t.getDate(),r=t.getMonth(),i=l.parts.length,d.length===i)for(o=0;i>o;o++)switch(h=parseInt(d[o],10)||1,l.parts[o]){case"dd":case"d":n=h;break;case"mm":case"m":r=h-1;break;case"yy":s=2e3+h;break;case"yyyy":s=h}return new Date(s,r,n)},formatDate:function(t){var e,i,s,n,r,h=this.format,o="";if(a(t))for(o=h.source,i=t.getFullYear(),n={d:t.getDate(),m:t.getMonth()+1,yy:i.toString().substring(2),yyyy:i},n.dd=(n.d<10?"0":"")+n.d,n.mm=(n.m<10?"0":"")+n.m,e=h.parts.length,r=0;e>r;r++)s=h.parts[r],o=o.replace(s,n[s]);return o},destroy:function(){this.unbind(),this.unbuild(),this.$element.removeData(p)}},d.LANGUAGES={},d.DEFAULTS={autoshow:!1,autohide:!1,autopick:!1,inline:!1,container:null,trigger:null,language:"",format:"mm/dd/yyyy",date:null,startDate:null,endDate:null,startView:0,weekStart:0,yearFirst:!1,yearSuffix:"",days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],itemTag:"li",mutedClass:"muted",pickedClass:"picked",disabledClass:"disabled",template:'
                                  ',offset:10,zIndex:1,filter:null,show:null,hide:null,pick:null},d.setDefaults=function(e){t.extend(d.DEFAULTS,t.isPlainObject(e)&&e)},d.other=t.fn.datepicker,t.fn.datepicker=function(i){var a,r=n(arguments,1);return this.each(function(){var s,n,h=t(this),o=h.data(p);if(!o){if(/destroy/.test(i))return;s=t.extend({},h.data(),t.isPlainObject(i)&&i),h.data(p,o=new d(this,s))}e(i)&&t.isFunction(n=o[i])&&(a=n.apply(o,r))}),s(a)?this:a},t.fn.datepicker.Constructor=d,t.fn.datepicker.languages=d.LANGUAGES,t.fn.datepicker.setDefaults=d.setDefaults,t.fn.datepicker.noConflict=function(){return t.fn.datepicker=d.other,this}}); \ No newline at end of file diff --git a/package.json b/package.json index d4b7da0..31b2e71 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,51 @@ { - "name": "datepicker", - "description": "A simple, lightweight, customizable jQuery datepicker plugin.", - "version": "0.1.0", - "keywords": [ - "image", - "cropping", - "jquery", - "plugin", - "html", - "css", - "javascript", - "front-end", - "web", - "development" - ], - "author": { - "name": "Fengyuan Chen", - "url": "https://github.com/fengyuanchen" - }, - "homepage": "https://github.com/fengyuanchen/datepicker", - "repository": { - "type": "git", - "url": "https://github.com/fengyuanchen/datepicker.git" - }, - "bugs": { - "url": "https://github.com/fengyuanchen/datepicker/issues" - }, - "license": { - "type": "MIT", - "url": "https://github.com/fengyuanchen/datepicker/blob/master/LICENSE.md" - }, - "dependencies": { - "jquery": ">= 1.9.0" - }, - "devDependencies": { - "grunt": "~0.4.5", - "grunt-autoprefixer": "~1.0.0", - "grunt-banner": "~0.2.3", - "grunt-contrib-clean": "~0.6.0", - "grunt-contrib-jshint": "~0.10.0", - "grunt-contrib-uglify": "~0.5.1", - "grunt-contrib-csslint": "~0.2.0", - "grunt-contrib-cssmin": "~0.10.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-watch": "~0.6.1", - "grunt-csscomb": "~2.0.1", - "load-grunt-tasks": "~0.6.0" - } + "name": "datepicker", + "description": "A simple jQuery datepicker plugin.", + "version": "0.2.0", + "main": "dist/datepicker.js", + "keywords": [ + "date", + "picker", + "datepicker", + "jquery", + "plugin", + "html", + "css", + "javascript", + "front-end", + "web", + "development" + ], + "author": { + "name": "Fengyuan Chen", + "url": "https://github.com/fengyuanchen" + }, + "homepage": "https://github.com/fengyuanchen/datepicker", + "repository": { + "type": "git", + "url": "https://github.com/fengyuanchen/datepicker.git" + }, + "bugs": { + "url": "https://github.com/fengyuanchen/datepicker/issues" + }, + "license": "MIT", + "dependencies": { + "jquery": ">= 1.9.1" + }, + "devDependencies": { + "gulp": "^3.9.0", + "gulp-autoprefixer": "^3.1.0", + "gulp-csscomb": "^3.0.6", + "gulp-csslint": "^0.2.0", + "gulp-htmlcomb": "0.1.0", + "gulp-jscs": "^3.0.0", + "gulp-jshint": "^1.11.0", + "gulp-load-plugins": "^1.0.0", + "gulp-minify-css": "^1.2.1", + "gulp-qunit": "^1.2.1", + "gulp-rename": "^1.2.0", + "gulp-replace": "^0.5.3", + "gulp-sass": "^2.0.4", + "gulp-uglify": "^1.4.2" + } }