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,
- '' + this.defaults.itemTag + '>'
- ].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 +
+ '' + itemTag + '>'
+ );
+ },
+
+ 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,""+this.defaults.itemTag+">"].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+""+s+">"},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"
+ }
}