├── .gitignore ├── src ├── themes │ └── default │ │ └── modules │ │ ├── calendar.overrides │ │ └── calendar.variables ├── theme.config ├── theme.less └── definitions │ └── modules │ ├── calendar.less │ └── calendar.js ├── bower.json ├── package.json ├── LICENSE ├── dist ├── calendar.min.css ├── calendar.css ├── calendar.min.js └── calendar.js ├── gulpfile.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /src/themes/default/modules/calendar.overrides: -------------------------------------------------------------------------------- 1 | /******************************* 2 | Theme Overrides 3 | *******************************/ 4 | -------------------------------------------------------------------------------- /src/theme.config: -------------------------------------------------------------------------------- 1 | @site : 'default'; 2 | @calendar : 'default'; 3 | 4 | @themesFolder : 'themes'; 5 | 6 | @import "theme.less"; 7 | 8 | /* End Config */ -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "semantic-ui-calendar", 3 | "version": "0.0.8", 4 | "main": [ 5 | "./dist/calendar.css", 6 | "./dist/calendar.js" 7 | ], 8 | "ignore": [ 9 | "node_modules", 10 | ".bowerrc", 11 | ".gitignore", 12 | ".jshintignore", 13 | ".jshintrc", 14 | "bower.json", 15 | "gulpfile.js", 16 | "package.json", 17 | "README.md" 18 | ] 19 | } -------------------------------------------------------------------------------- /src/theme.less: -------------------------------------------------------------------------------- 1 | @theme: @@element; 2 | 3 | @import "@{themesFolder}/default/globals/site.variables"; 4 | @import "@{themesFolder}/@{site}/globals/site.variables"; 5 | 6 | @import "@{themesFolder}/default/@{type}s/@{element}.variables"; 7 | @import (optional) "@{themesFolder}/@{theme}/@{type}s/@{element}.variables"; 8 | 9 | .loadUIOverrides() { 10 | @import (optional) "@{themesFolder}/@{theme}/@{type}s/@{element}.overrides"; 11 | } 12 | -------------------------------------------------------------------------------- /src/themes/default/modules/calendar.variables: -------------------------------------------------------------------------------- 1 | /******************************* 2 | Calendar 3 | *******************************/ 4 | 5 | @focusBoxShadow: inset 0 0 0 1px @focusedFormBorderColor; 6 | @focusInvertedBoxShadow: inset 0 0 0 1px @focusedFormBorderColor; 7 | 8 | @todayFontWeight: bold; 9 | 10 | @rangeBackground: @transparentBlack; 11 | @rangeTextColor: @selectedTextColor; 12 | @rangeBoxShadow: none; 13 | @rangeInvertedBackground: @transparentWhite; 14 | @rangeInvertedTextColor: @invertedSelectedTextColor; 15 | @rangeInvertedBoxShadow: none; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "semantic-ui-calendar", 3 | "version": "0.0.8", 4 | "description": "Calendar module for Semantic UI", 5 | "author": "Michael de Hoog (https://github.com/mdehoog/)", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/mdehoog/semantic-ui-calendar.git" 9 | }, 10 | "license": "MIT", 11 | "devDependencies": { 12 | "gulp": "^3.9.1", 13 | "gulp-autoprefixer": "^3.1.1", 14 | "gulp-clean-css": "^2.0.13", 15 | "gulp-clone": "^1.0.0", 16 | "gulp-flatten": "^0.3.1", 17 | "gulp-less": "^3.1.0", 18 | "gulp-rename": "^1.2.2", 19 | "gulp-replace": "^0.5.4", 20 | "gulp-uglify": "^2.0.0", 21 | "require-dot-file": "^0.4.0", 22 | "semantic-ui-less": "^2.2.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Michael de Hoog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /dist/calendar.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 0.0.8 - Calendar 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */.ui.calendar .ui.popup{max-width:none;padding:0;border:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.calendar .calendar:focus{outline:0}.ui.calendar .ui.popup .ui.grid{display:block;white-space:nowrap}.ui.calendar .ui.popup .ui.grid>.column{width:auto}.ui.calendar .ui.table.minute,.ui.calendar .ui.table.month,.ui.calendar .ui.table.year{min-width:15em}.ui.calendar .ui.table.day{min-width:18em}.ui.calendar .ui.table.hour{min-width:20em}.ui.calendar .ui.table tr td,.ui.calendar .ui.table tr th{padding:.5em;white-space:nowrap}.ui.calendar .ui.table tr th{border-left:none}.ui.calendar .ui.table tr th .icon{margin:0}.ui.calendar .ui.table tr:first-child th{position:relative;padding-left:0;padding-right:0}.ui.calendar .ui.table.day tr:first-child th{border:none}.ui.calendar .ui.table.day tr:nth-child(2) th{padding-top:.2em;padding-bottom:.3em}.ui.calendar .ui.table tr td{padding-left:.1em;padding-right:.1em}.ui.calendar .ui.table tr .link{cursor:pointer}.ui.calendar .ui.table tr .prev.link{width:14.28571429%;position:absolute;left:0}.ui.calendar .ui.table tr .next.link{width:14.28571429%;position:absolute;right:0}.ui.calendar .ui.table tr .disabled{pointer-events:none;color:rgba(40,40,40,.3)}.ui.calendar .ui.table tr td.today{font-weight:700}.ui.calendar .ui.table tr td.range{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);box-shadow:none}.ui.calendar .ui.table.inverted tr td.range{background:rgba(255,255,255,.08);color:#fff;box-shadow:none}.ui.calendar .calendar.active .ui.table tbody tr td.focus,.ui.calendar .calendar.active .ui.table.inverted tbody tr td.focus,.ui.calendar .calendar:focus .ui.table tbody tr td.focus,.ui.calendar .calendar:focus .ui.table.inverted tbody tr td.focus{box-shadow:inset 0 0 0 1px #85B7D9} -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var less = require('gulp-less'); 3 | var autoprefixer = require('gulp-autoprefixer'); 4 | var flatten = require('gulp-flatten'); 5 | var clone = require('gulp-clone'); 6 | var replace = require('gulp-replace'); 7 | var minifyCSS = require('gulp-clean-css'); 8 | var rename = require('gulp-rename'); 9 | var uglify = require('gulp-uglify'); 10 | var path = require('path'); 11 | var npmPackage = require('./package.json'); 12 | 13 | var settings = { 14 | less: { 15 | paths: [ 16 | path.join(__dirname, 'node_modules', 'semantic-ui-less') 17 | ] 18 | }, 19 | 20 | /* What Browsers to Prefix */ 21 | prefix: { 22 | browsers: [ 23 | 'last 2 versions', 24 | '> 1%', 25 | 'opera 12.1', 26 | 'bb 10', 27 | 'android 4' 28 | ] 29 | }, 30 | 31 | /* File Renames */ 32 | rename: { 33 | minJS : { extname : '.min.js' }, 34 | minCSS : { extname : '.min.css' }, 35 | rtlCSS : { extname : '.rtl.css' }, 36 | rtlMinCSS : { extname : '.rtl.min.css' } 37 | }, 38 | 39 | /* Minified CSS Concat */ 40 | minify: { 41 | processImport : false, 42 | restructuring : false, 43 | keepSpecialComments : 1, 44 | roundingPrecision : -1, 45 | }, 46 | 47 | /* Minified JS Settings */ 48 | uglify: { 49 | mangle : true, 50 | preserveComments : 'some' 51 | } 52 | }; 53 | 54 | var comments = { 55 | // remove all comments from config files (.variable) 56 | variables : { 57 | in : /(\/\*[\s\S]+?\*\/+)[\s\S]+?\/\* End Config \*\//, 58 | out : '$1', 59 | }, 60 | // add version to first comment 61 | license: { 62 | in : /(^\/\*[\s\S]+)(# Semantic UI )([\s\S]+?\*\/)/, 63 | out : '$1$2' + npmPackage.version + ' $3' 64 | }, 65 | // adds uniform spacing around comments 66 | large: { 67 | in : /(\/\*\*\*\*[\s\S]+?\*\/)/mg, 68 | out : '\n\n$1\n' 69 | }, 70 | small: { 71 | in : /(\/\*---[\s\S]+?\*\/)/mg, 72 | out : '\n$1\n' 73 | }, 74 | tiny: { 75 | in : /(\/\* [\s\S]+? \*\/)/mg, 76 | out : '\n$1' 77 | } 78 | }; 79 | 80 | gulp.task('default', function() { 81 | gulp.start('less', 'javascript'); 82 | }); 83 | 84 | gulp.task('less', function () { 85 | return gulp.src('./src/definitions/**/*.less') 86 | .pipe(less(settings.less)) 87 | .pipe(autoprefixer(settings.prefix)) 88 | .pipe(replace(comments.variables.in, comments.variables.out)) 89 | .pipe(replace(comments.license.in, comments.license.out)) 90 | .pipe(replace(comments.large.in, comments.large.out)) 91 | .pipe(replace(comments.small.in, comments.small.out)) 92 | .pipe(replace(comments.tiny.in, comments.tiny.out)) 93 | .pipe(flatten()) 94 | .pipe(gulp.dest('./dist')) 95 | .pipe(minifyCSS(settings.minify)) 96 | .pipe(rename(settings.rename.minCSS)) 97 | .pipe(gulp.dest('./dist')); 98 | }); 99 | 100 | gulp.task('javascript', function () { 101 | return gulp.src('./src/definitions/**/*.js') 102 | .pipe(flatten()) 103 | .pipe(replace(comments.license.in, comments.license.out)) 104 | .pipe(gulp.dest('./dist')) 105 | .pipe(uglify(settings.uglify)) 106 | .pipe(rename(settings.rename.minJS)) 107 | .pipe(gulp.dest('./dist')); 108 | }); 109 | -------------------------------------------------------------------------------- /dist/calendar.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI 0.0.8 - Calendar 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | 12 | /******************************* 13 | Popup 14 | *******************************/ 15 | 16 | .ui.calendar .ui.popup { 17 | max-width: none; 18 | padding: 0; 19 | border: none; 20 | -webkit-user-select: none; 21 | -moz-user-select: none; 22 | -ms-user-select: none; 23 | user-select: none; 24 | } 25 | 26 | 27 | /******************************* 28 | Calendar 29 | *******************************/ 30 | 31 | .ui.calendar .calendar:focus { 32 | outline: 0; 33 | } 34 | 35 | 36 | /******************************* 37 | Grid 38 | *******************************/ 39 | 40 | .ui.calendar .ui.popup .ui.grid { 41 | display: block; 42 | white-space: nowrap; 43 | } 44 | .ui.calendar .ui.popup .ui.grid > .column { 45 | width: auto; 46 | } 47 | 48 | 49 | /******************************* 50 | Table 51 | *******************************/ 52 | 53 | .ui.calendar .ui.table.year, 54 | .ui.calendar .ui.table.month, 55 | .ui.calendar .ui.table.minute { 56 | min-width: 15em; 57 | } 58 | .ui.calendar .ui.table.day { 59 | min-width: 18em; 60 | } 61 | .ui.calendar .ui.table.hour { 62 | min-width: 20em; 63 | } 64 | .ui.calendar .ui.table tr th, 65 | .ui.calendar .ui.table tr td { 66 | padding: 0.5em; 67 | white-space: nowrap; 68 | } 69 | .ui.calendar .ui.table tr th { 70 | border-left: none; 71 | } 72 | .ui.calendar .ui.table tr th .icon { 73 | margin: 0; 74 | } 75 | .ui.calendar .ui.table tr th .icon { 76 | margin: 0; 77 | } 78 | .ui.calendar .ui.table tr:first-child th { 79 | position: relative; 80 | padding-left: 0; 81 | padding-right: 0; 82 | } 83 | .ui.calendar .ui.table.day tr:first-child th { 84 | border: none; 85 | } 86 | .ui.calendar .ui.table.day tr:nth-child(2) th { 87 | padding-top: 0.2em; 88 | padding-bottom: 0.3em; 89 | } 90 | .ui.calendar .ui.table tr td { 91 | padding-left: 0.1em; 92 | padding-right: 0.1em; 93 | } 94 | .ui.calendar .ui.table tr .link { 95 | cursor: pointer; 96 | } 97 | .ui.calendar .ui.table tr .prev.link { 98 | width: 14.28571429%; 99 | position: absolute; 100 | left: 0; 101 | } 102 | .ui.calendar .ui.table tr .next.link { 103 | width: 14.28571429%; 104 | position: absolute; 105 | right: 0; 106 | } 107 | .ui.calendar .ui.table tr .disabled { 108 | pointer-events: none; 109 | color: rgba(40, 40, 40, 0.3); 110 | } 111 | 112 | /*-------------- 113 | States 114 | ---------------*/ 115 | 116 | .ui.calendar .ui.table tr td.today { 117 | font-weight: bold; 118 | } 119 | .ui.calendar .ui.table tr td.range { 120 | background: rgba(0, 0, 0, 0.05); 121 | color: rgba(0, 0, 0, 0.95); 122 | box-shadow: none; 123 | } 124 | .ui.calendar .ui.table.inverted tr td.range { 125 | background: rgba(255, 255, 255, 0.08); 126 | color: #ffffff; 127 | box-shadow: none; 128 | } 129 | .ui.calendar .calendar:focus .ui.table tbody tr td.focus, 130 | .ui.calendar .calendar.active .ui.table tbody tr td.focus { 131 | box-shadow: inset 0 0 0 1px #85B7D9; 132 | } 133 | .ui.calendar .calendar:focus .ui.table.inverted tbody tr td.focus, 134 | .ui.calendar .calendar.active .ui.table.inverted tbody tr td.focus { 135 | box-shadow: inset 0 0 0 1px #85B7D9; 136 | } 137 | 138 | 139 | /******************************* 140 | Theme Overrides 141 | *******************************/ 142 | 143 | -------------------------------------------------------------------------------- /src/definitions/modules/calendar.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Semantic UI - Calendar 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | * 9 | */ 10 | 11 | /******************************* 12 | Theme 13 | *******************************/ 14 | 15 | @type : 'module'; 16 | @element : 'calendar'; 17 | 18 | @import (multiple) '../../theme.config'; 19 | 20 | /******************************* 21 | Popup 22 | *******************************/ 23 | 24 | .ui.calendar .ui.popup { 25 | max-width: none; 26 | padding: 0; 27 | border: none; 28 | user-select: none; 29 | } 30 | 31 | /******************************* 32 | Calendar 33 | *******************************/ 34 | 35 | .ui.calendar .calendar:focus { 36 | outline: 0; 37 | } 38 | 39 | /******************************* 40 | Grid 41 | *******************************/ 42 | 43 | .ui.calendar .ui.popup .ui.grid { 44 | display: block; 45 | white-space: nowrap; 46 | } 47 | 48 | .ui.calendar .ui.popup .ui.grid > .column { 49 | width: auto; 50 | } 51 | 52 | /******************************* 53 | Table 54 | *******************************/ 55 | 56 | .ui.calendar .ui.table.year, 57 | .ui.calendar .ui.table.month, 58 | .ui.calendar .ui.table.minute { 59 | min-width: 15em; 60 | } 61 | 62 | .ui.calendar .ui.table.day { 63 | min-width: 18em; 64 | } 65 | 66 | .ui.calendar .ui.table.hour { 67 | min-width: 20em; 68 | } 69 | 70 | .ui.calendar .ui.table tr th, 71 | .ui.calendar .ui.table tr td { 72 | padding: 0.5em; 73 | white-space: nowrap; 74 | } 75 | 76 | .ui.calendar .ui.table tr th { 77 | border-left: none; 78 | } 79 | 80 | .ui.calendar .ui.table tr th .icon { 81 | margin: 0; 82 | } 83 | 84 | .ui.calendar .ui.table tr th .icon { 85 | margin: 0; 86 | } 87 | 88 | .ui.calendar .ui.table tr:first-child th { 89 | position: relative; 90 | padding-left: 0; 91 | padding-right: 0; 92 | } 93 | 94 | .ui.calendar .ui.table.day tr:first-child th { 95 | border: none; 96 | } 97 | 98 | .ui.calendar .ui.table.day tr:nth-child(2) th { 99 | padding-top: 0.2em; 100 | padding-bottom: 0.3em; 101 | } 102 | 103 | .ui.calendar .ui.table tr td { 104 | padding-left: 0.1em; 105 | padding-right: 0.1em; 106 | } 107 | 108 | .ui.calendar .ui.table tr .link { 109 | cursor: pointer; 110 | } 111 | 112 | .ui.calendar .ui.table tr .prev.link { 113 | width: 14.28571429%; 114 | position: absolute; 115 | left: 0; 116 | } 117 | 118 | .ui.calendar .ui.table tr .next.link { 119 | width: 14.28571429%; 120 | position: absolute; 121 | right: 0; 122 | } 123 | 124 | .ui.calendar .ui.table tr .disabled { 125 | pointer-events: none; 126 | color: @disabledTextColor; 127 | } 128 | 129 | /*-------------- 130 | States 131 | ---------------*/ 132 | 133 | .ui.calendar .ui.table tr td.today { 134 | font-weight: @todayFontWeight; 135 | } 136 | 137 | .ui.calendar .ui.table tr td.range { 138 | background: @rangeBackground; 139 | color: @rangeTextColor; 140 | box-shadow: @rangeBoxShadow; 141 | } 142 | 143 | .ui.calendar .ui.table.inverted tr td.range { 144 | background: @rangeInvertedBackground; 145 | color: @rangeInvertedTextColor; 146 | box-shadow: @rangeInvertedBoxShadow; 147 | } 148 | 149 | .ui.calendar .calendar:focus .ui.table tbody tr td.focus, 150 | .ui.calendar .calendar.active .ui.table tbody tr td.focus { 151 | box-shadow: @focusBoxShadow; 152 | } 153 | 154 | .ui.calendar .calendar:focus .ui.table.inverted tbody tr td.focus, 155 | .ui.calendar .calendar.active .ui.table.inverted tbody tr td.focus { 156 | box-shadow: @focusInvertedBoxShadow; 157 | } 158 | 159 | .loadUIOverrides(); 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No longer maintained. See [Fomantic-UI](https://github.com/fomantic/Fomantic-UI) for a more recently updated version of this module. 2 | 3 | # Semantic-UI-Calendar 4 | 5 | Calendar module for Semantic UI. See https://jsbin.com/ruqakehefa/ for example usage. 6 | 7 | This was originally a PR for Semantic UI: https://github.com/Semantic-Org/Semantic-UI/pull/3256. 8 | 9 | ## Installation 10 | 11 | Install using bower: 12 | 13 | ``` 14 | bower install --save semantic-ui-calendar 15 | ``` 16 | 17 | Install using npm: 18 | 19 | ``` 20 | npm install --save semantic-ui-calendar 21 | ``` 22 | 23 | Include javascript and css in html: 24 | 25 | ```html 26 | 27 | 28 | ``` 29 | 30 | ## Compiling CSS from LESS 31 | 32 | If you want to theme the calendar, or change some of the variables from the default theme, you can compile the LESS source using your favourite build tool. 33 | 34 | Import the calendar.less file into your app. 35 | 36 | ```less 37 | @import 'definitions/modules/calendar'; 38 | ``` 39 | 40 | Ensure that this module's `src` directory is included when compiling the LESS: 41 | 42 | ```javascript 43 | lessOptions: { 44 | paths: [ 45 | 'bower_components/semantic-ui-calendar/src', 46 | ... 47 | ] 48 | } 49 | ``` 50 | 51 | ## Behavior 52 | 53 | These functions can be called the same way you call Semantic UI behavior functions: 54 | 55 | ```javascript 56 | $('#mycalendar').calendar('behavior name', argumentOne, argumentTwo); 57 | ``` 58 | 59 | Behavior | Description 60 | --- | --- 61 | `refresh` | Refresh the calendar. 62 | `popup(arguments)` | Call the popup module (e.g. passing 'show' will show the calendar popup). 63 | `focus` | Focus the calendar input. 64 | `blur` | Blur the calendar input. 65 | `clear` | Clear the selected date. 66 | `get date` | Get the selected date. 67 | `set date(date, updateInput = true, fireChange = true)` | Set the selected date. Pass `false` to `updateInput` to disable updating the input. Pass `false` to `fireChange` to disable the `onChange` callback for this change. 68 | `get mode` | Get the current selection mode (`year`, `month`, `day`, `hour`, `minute`). 69 | `set mode(mode)` | Set the current selection mode (`year`, `month`, `day`, `hour`, `minute`). 70 | `get startDate` | Get the start date for range selection. 71 | `set startDate(date)` | Set the start date for range selection. 72 | `get endDate` | Get the end date for range selection. 73 | `set endDate(date)` | Set the end date for range selection. 74 | `get focusDate` | Get the currently focused date. 75 | `set focusDate(date)` | Set the currently focused date. 76 | 77 | ## Settings 78 | 79 | The following settings are supported by this module: 80 | 81 | ```javascript 82 | type: 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year' 83 | firstDayOfWeek: 0, // day for first day column (0 = Sunday) 84 | constantHeight: true, // add rows to shorter months to keep day calendar height consistent (6 rows) 85 | today: false, // show a 'today/now' button at the bottom of the calendar 86 | closable: true, // close the popup after selecting a date/time 87 | monthFirst: true, // month before day when parsing/converting date from/to text 88 | touchReadonly: true, // set input to readonly on touch devices 89 | inline: false, // create the calendar inline instead of inside a popup 90 | on: null, // when to show the popup (defaults to 'focus' for input, 'click' for others) 91 | initialDate: null, // date to display initially when no date is selected (null = now) 92 | startMode: false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day') 93 | minDate: null, // minimum date/time that can be selected, dates/times before are disabled 94 | maxDate: null, // maximum date/time that can be selected, dates/times after are disabled 95 | ampm: true, // show am/pm in time mode 96 | disableYear: false, // disable year selection mode 97 | disableMonth: false, // disable month selection mode 98 | disableMinute: false, // disable minute selection mode 99 | formatInput: true, // format the input text upon input blur and module creation 100 | startCalendar: null, // jquery object or selector for another calendar that represents the start date of a date range 101 | endCalendar: null, // jquery object or selector for another calendar that represents the end date of a date range 102 | multiMonth: 1, // show multiple months when in 'day' mode 103 | 104 | // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden) 105 | popupOptions: { 106 | position: 'bottom left', 107 | lastResort: 'bottom left', 108 | prefer: 'opposite', 109 | hideOnScroll: false 110 | }, 111 | 112 | text: { 113 | days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 114 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 115 | monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 116 | today: 'Today', 117 | now: 'Now', 118 | am: 'AM', 119 | pm: 'PM' 120 | }, 121 | 122 | formatter: { 123 | header: function (date, mode, settings) { 124 | //return a string to show on the header for the given 'date' and 'mode' 125 | }, 126 | yearHeader: function (date, settings) { 127 | //return a string to show on the header for the given 'date' in year mode 128 | }, 129 | monthHeader: function (date, settings) { 130 | //return a string to show on the header for the given 'date' in month mode 131 | }, 132 | dayHeader: function (date, settings) { 133 | //return a string to show on the header for the given 'date' in day mode 134 | }, 135 | hourHeader: function (date, settings) { 136 | //return a string to show on the header for the given 'date' in hour mode 137 | }, 138 | minuteHeader: function (date, settings) { 139 | //return a string to show on the header for the given 'date' in minute mode 140 | }, 141 | dayColumnHeader: function (day, settings) { 142 | //return a abbreviated day string to show above each column in day mode 143 | }, 144 | datetime: function (date, settings) { 145 | //return a formatted string representing the date & time of 'date' 146 | }, 147 | date: function (date, settings) { 148 | //return a formatted string representing the date of 'date' 149 | }, 150 | time: function (date, settings, forCalendar) { 151 | //return a formatted string representing the time of 'date' 152 | }, 153 | today: function (settings) { 154 | return settings.type === 'date' ? settings.text.today : settings.text.now; 155 | }, 156 | cell: function (cell, date, cellOptions) { 157 | //customize the calendar cell, cellOptions is: 158 | //{ mode: string, adjacent: boolean, disabled: boolean, active: boolean, today: boolean } 159 | } 160 | }, 161 | 162 | parser: { 163 | date: function (text, settings) { 164 | //return a date parsed from 'text' 165 | } 166 | }, 167 | 168 | // callback when date changes, return false to cancel the change 169 | onChange: function (date, text, mode) { 170 | }, 171 | 172 | // callback before show animation, return false to prevent show 173 | onShow: function () { 174 | }, 175 | 176 | // callback after show animation 177 | onVisible: function () { 178 | }, 179 | 180 | // callback before hide animation, return false to prevent hide 181 | onHide: function () { 182 | }, 183 | 184 | // callback after hide animation 185 | onHidden: function () { 186 | }, 187 | 188 | // is the given date disabled? 189 | isDisabled: function (date, mode) { 190 | return false; 191 | }, 192 | 193 | selector: { 194 | popup: '.ui.popup', 195 | input: 'input', 196 | activator: 'input' 197 | }, 198 | 199 | regExp: { 200 | dateWords: /[^A-Za-z\u00C0-\u024F]+/g, 201 | dateNumbers: /[^\d:]+/g 202 | }, 203 | 204 | error: { 205 | popup: 'UI Popup, a required component is not included in this page', 206 | method: 'The method you called is not defined.' 207 | }, 208 | 209 | className: { 210 | calendar: 'calendar', 211 | active: 'active', 212 | popup: 'ui popup', 213 | grid: 'ui equal width grid', 214 | column: 'column', 215 | table: 'ui celled center aligned unstackable table', 216 | prev: 'prev link', 217 | next: 'next link', 218 | prevIcon: 'chevron left icon', 219 | nextIcon: 'chevron right icon', 220 | link: 'link', 221 | cell: 'link', 222 | disabledCell: 'disabled', 223 | adjacentCell: 'adjacent', 224 | activeCell: 'active', 225 | rangeCell: 'range', 226 | focusCell: 'focus', 227 | todayCell: 'today', 228 | today: 'today link' 229 | }, 230 | 231 | metadata: { 232 | date: 'date', 233 | focusDate: 'focusDate', 234 | startDate: 'startDate', 235 | endDate: 'endDate', 236 | mode: 'mode', 237 | monthOffset: 'monthOffset' 238 | } 239 | ``` 240 | -------------------------------------------------------------------------------- /dist/calendar.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t,a,n){"use strict";t="undefined"!=typeof t&&t.Math==Math?t:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")(),e.fn.calendar=function(t){var o,r=e(this),i=r.selector||"",l=(new Date).getTime(),d=[],s=arguments[0],u="string"==typeof s,p=[].slice.call(arguments,1);return r.each(function(){var r,c,f=e.isPlainObject(t)?e.extend(!0,{},e.fn.calendar.settings,t):e.extend({},e.fn.calendar.settings),h=f.className,g=f.namespace,m=f.selector,v=f.formatter,y=f.parser,D=f.metadata,b=f.error,C="."+g,M="module-"+g,w=e(this),x=w.find(m.input),k=w.find(m.popup),T=w.find(m.activator),F=this,H=w.data(M),O=!1,I=!1;c={initialize:function(){c.debug("Initializing calendar for",F),r=c.get.isTouch(),c.setup.popup(),c.setup.inline(),c.setup.input(),c.setup.date(),c.create.calendar(),c.bind.events(),c.instantiate()},instantiate:function(){c.verbose("Storing instance of calendar"),H=c,w.data(M,H)},destroy:function(){c.verbose("Destroying previous calendar for",F),w.removeData(M),c.unbind.events()},setup:{popup:function(){if(!f.inline&&(T.length||(T=w.children().first(),T.length))){if(e.fn.popup===n)return void c.error(b.popup);k.length||(k=e("
").addClass(h.popup).prependTo(T.parent())),k.addClass(h.calendar);var t=f.onVisible,a=f.onHidden;x.length||(k.attr("tabindex","0"),t=function(){return c.focus(),f.onVisible.apply(k,arguments)},a=function(){return c.blur(),f.onHidden.apply(k,arguments)});var o=function(){return c.set.focusDate(c.get.date()),c.set.mode(f.startMode),f.onShow.apply(k,arguments)},r=f.on||(x.length?"focus":"click"),i=e.extend({},f.popupOptions,{popup:k,on:r,hoverable:"hover"===r,onShow:o,onVisible:t,onHide:f.onHide,onHidden:a});c.popup(i)}},inline:function(){T.length&&!f.inline||(k=e("
").addClass(h.calendar).appendTo(w),x.length||k.attr("tabindex","0"))},input:function(){f.touchReadonly&&x.length&&r&&x.prop("readonly",!0)},date:function(){if(x.length){var e=x.val(),t=y.date(e,f);c.set.date(t,f.formatInput,!1)}}},create:{calendar:function(){var t,a,n,o,r,i,l,d=c.get.mode(),s=new Date,u=c.get.date(),p=c.get.focusDate(),g=p||u||f.initialDate||s;g=c.helper.dateInRange(g),p||(p=g,c.set.focusDate(p,!1,!1));var m="year"===d,y="month"===d,b="day"===d,C="hour"===d,M="minute"===d,w="time"===f.type,x=Math.max(f.multiMonth,1),T=b?c.get.monthOffset():0,F=g.getMinutes(),H=g.getHours(),O=g.getDate(),I=g.getMonth()+T,N=g.getFullYear(),Y=b?7:C?4:3,E=7===Y?"seven":4===Y?"four":"three",R=b||C?6:4,j=b?x:1,A=k;for(A.empty(),j>1&&(l=e("
").addClass(h.grid).appendTo(A)),o=0;o1){var S=e("
").addClass(h.column).appendTo(l);A=S}var V=I+o,P=(new Date(N,V,1).getDay()-f.firstDayOfWeek%7+7)%7;if(!f.constantHeight&&b){var q=new Date(N,V+1,0).getDate()+P;R=Math.ceil(q/7)}var K=m?10:y?1:0,J=b?1:0,W=C||M?1:0,z=C||M?O:1,L=new Date(N-K,V-J,z-W,H),B=new Date(N+K,V+J,z+W,H),U=m?new Date(10*Math.ceil(N/10)-9,0,0):y?new Date(N,0,0):b?new Date(N,V,0):new Date(N,V,O,(-1)),Q=m?new Date(10*Math.ceil(N/10)+1,0,1):y?new Date(N+1,0,1):b?new Date(N,V+1,1):new Date(N,V,O+1),Z=e("").addClass(h.table).addClass(E+" column").addClass(d).appendTo(A);if(!w){var G=e("").appendTo(Z);r=e("").appendTo(G),i=e("").appendTo(G),t=0;t").appendTo(r),i.text(v.dayColumnHeader((t+f.firstDayOfWeek)%7,f))}var ae=e("").appendTo(Z);for(t=m?10*Math.ceil(N/10)-9:b?1-P:0,a=0;a").appendTo(ae),n=0;n").addClass(h.cell).appendTo(r),i.text(oe),i.data(D.date,ne);var re=b&&ne.getMonth()!==(V+12)%12,ie=re||!c.helper.isDateInRange(ne,d)||f.isDisabled(ne,d),le=c.helper.dateEqual(ne,u,d),de=c.helper.dateEqual(ne,s,d);i.toggleClass(h.adjacentCell,re),i.toggleClass(h.disabledCell,ie),i.toggleClass(h.activeCell,le&&!re),C||M||i.toggleClass(h.todayCell,!re&&de);var se={mode:d,adjacent:re,disabled:ie,active:le,today:de};v.cell(i,ne,se),c.helper.dateEqual(ne,p,d)&&c.set.focusDate(ne,!1,!1)}if(f.today){var ue=e("").appendTo(ae),pe=e("
").attr("colspan",""+Y).appendTo(r);var X=m||y?new Date(N,0,1):b?new Date(N,V,1):new Date(N,V,O,H,F),$=e("").addClass(h.link).appendTo(i);$.text(v.header(X,d,f));var _=y?f.disableYear?"day":"year":b?f.disableMonth?"year":"month":"day";if($.data(D.mode,_),0===o){var ee=e("").addClass(h.prev).appendTo(i);ee.data(D.focusDate,L),ee.toggleClass(h.disabledCell,!c.helper.isDateInRange(U,d)),e("").addClass(h.prevIcon).appendTo(ee)}if(o===j-1){var te=e("").addClass(h.next).appendTo(i);te.data(D.focusDate,B),te.toggleClass(h.disabledCell,!c.helper.isDateInRange(Q,d)),e("").addClass(h.nextIcon).appendTo(te)}if(b)for(r=e("
").attr("colspan",""+Y).addClass(h.today).appendTo(ue);pe.text(v.today(f)),pe.data(D.date,s)}c.update.focus(!1,Z)}}},update:{focus:function(t,a){a=a||k;var n=c.get.mode(),o=c.get.date(),i=c.get.focusDate(),l=c.get.startDate(),d=c.get.endDate(),s=(t?i:null)||o||(r?null:i);a.find("td").each(function(){var t=e(this),a=t.data(D.date);if(a){var o=t.hasClass(h.disabledCell),u=t.hasClass(h.activeCell),p=t.hasClass(h.adjacentCell),f=c.helper.dateEqual(a,i,n),g=!!s&&(!!l&&c.helper.isDateInRange(a,n,l,s)||!!d&&c.helper.isDateInRange(a,n,s,d));t.toggleClass(h.focusCell,f&&(!r||O)&&!p),t.toggleClass(h.rangeCell,g&&!u&&!o)}})}},refresh:function(){c.create.calendar()},bind:{events:function(){k.on("mousedown"+C,c.event.mousedown),k.on("touchstart"+C,c.event.mousedown),k.on("mouseup"+C,c.event.mouseup),k.on("touchend"+C,c.event.mouseup),k.on("mouseover"+C,c.event.mouseover),x.length?(x.on("input"+C,c.event.inputChange),x.on("focus"+C,c.event.inputFocus),x.on("blur"+C,c.event.inputBlur),x.on("click"+C,c.event.inputClick),x.on("keydown"+C,c.event.keydown)):k.on("keydown"+C,c.event.keydown)}},unbind:{events:function(){k.off(C),x.length&&x.off(C)}},event:{mouseover:function(t){var a=e(t.target),n=a.data(D.date),o=1===t.buttons;n&&c.set.focusDate(n,!1,!0,o)},mousedown:function(t){x.length&&t.preventDefault(),O=t.type.indexOf("touch")>=0;var a=e(t.target),n=a.data(D.date);n&&c.set.focusDate(n,!1,!0,!0)},mouseup:function(t){c.focus(),t.preventDefault(),t.stopPropagation(),O=!1;var a=e(t.target),n=a.parent();(n.data(D.date)||n.data(D.focusDate)||n.data(D.mode))&&(a=n);var o=a.data(D.date),r=a.data(D.focusDate),i=a.data(D.mode);if(o){var l=a.hasClass(h.today);c.selectDate(o,l)}else r?c.set.focusDate(r):i&&c.set.mode(i)},keydown:function(e){if(27!==e.keyCode&&9!==e.keyCode||c.popup("hide"),c.popup("is visible"))if(37===e.keyCode||38===e.keyCode||39===e.keyCode||40===e.keyCode){var t=c.get.mode(),a="day"===t?7:"hour"===t?4:3,n=37===e.keyCode?-1:38===e.keyCode?-a:39==e.keyCode?1:a;n*="minute"===t?5:1;var o=c.get.focusDate()||c.get.date()||new Date,r=o.getFullYear()+("year"===t?n:0),i=o.getMonth()+("month"===t?n:0),l=o.getDate()+("day"===t?n:0),d=o.getHours()+("hour"===t?n:0),s=o.getMinutes()+("minute"===t?n:0),u=new Date(r,i,l,d,s);"time"===f.type&&(u=c.helper.mergeDateTime(o,u)),c.helper.isDateInRange(u,t)&&c.set.focusDate(u)}else if(13===e.keyCode){var t=c.get.mode(),p=c.get.focusDate();p&&!f.isDisabled(p,t)&&c.selectDate(p),e.preventDefault(),e.stopPropagation()}38!==e.keyCode&&40!==e.keyCode||(e.preventDefault(),c.popup("show"))},inputChange:function(){var e=x.val(),t=y.date(e,f);c.set.date(t,!1)},inputFocus:function(){k.addClass(h.active)},inputBlur:function(){if(k.removeClass(h.active),f.formatInput){var e=c.get.date(),t=v.datetime(e,f);x.val(t)}},inputClick:function(){c.popup("show")}},get:{date:function(){return w.data(D.date)||null},focusDate:function(){return w.data(D.focusDate)||null},startDate:function(){var e=c.get.calendarModule(f.startCalendar);return(e?e.get.date():w.data(D.startDate))||null},endDate:function(){var e=c.get.calendarModule(f.endCalendar);return(e?e.get.date():w.data(D.endDate))||null},monthOffset:function(){return w.data(D.monthOffset)||0},mode:function(){var t=w.data(D.mode)||f.startMode,a=c.get.validModes();return e.inArray(t,a)>=0?t:"time"===f.type?"hour":"month"===f.type?"month":"year"===f.type?"year":"day"},validModes:function(){var e=[];return"time"!==f.type&&(f.disableYear&&"year"!==f.type||e.push("year"),(!f.disableMonth&&"year"!==f.type||"month"===f.type)&&e.push("month"),f.type.indexOf("date")>=0&&e.push("day")),f.type.indexOf("time")>=0&&(e.push("hour"),f.disableMinute||e.push("minute")),e},isTouch:function(){try{return a.createEvent("TouchEvent"),!0}catch(e){return!1}},calendarModule:function(t){return t?(t instanceof e||(t=w.parent().children(t).first()),t.data(M)):null}},set:{date:function(e,t,a){t=t!==!1,a=a!==!1,e=c.helper.sanitiseDate(e),e=c.helper.dateInRange(e);var o=c.get.mode(),r=v.datetime(e,f);if(a&&f.onChange.call(F,e,r,o)===!1)return!1;if(c.set.focusDate(e),f.isDisabled(e,o))return!1;var i=c.get.endDate();i&&e&&e>i&&c.set.endDate(n),c.set.dataKeyValue(D.date,e),t&&x.length&&x.val(r)},startDate:function(e,t){e=c.helper.sanitiseDate(e);var a=c.get.calendarModule(f.startCalendar);a&&a.set.date(e),c.set.dataKeyValue(D.startDate,e,t)},endDate:function(e,t){e=c.helper.sanitiseDate(e);var a=c.get.calendarModule(f.endCalendar);a&&a.set.date(e),c.set.dataKeyValue(D.endDate,e,t)},focusDate:function(e,t,a,n){e=c.helper.sanitiseDate(e),e=c.helper.dateInRange(e);var o="day"===c.get.mode(),r=c.get.focusDate();if(o&&e&&r){var i=e.getFullYear()-r.getFullYear(),l=12*i+e.getMonth()-r.getMonth();if(l){var d=c.get.monthOffset()-l;c.set.monthOffset(d,!1)}}var s=c.set.dataKeyValue(D.focusDate,e,t);a=a!==!1&&s&&t===!1||I!=n,I=n,a&&c.update.focus(n)},monthOffset:function(e,t){var a=Math.max(f.multiMonth,1);e=Math.max(1-a,Math.min(0,e)),c.set.dataKeyValue(D.monthOffset,e,t)},mode:function(e,t){c.set.dataKeyValue(D.mode,e,t)},dataKeyValue:function(e,t,a){var n=w.data(e),o=n===t||n<=t&&n>=t;return t?w.data(e,t):w.removeData(e),a=a!==!1&&!o,a&&c.create.calendar(),!o}},selectDate:function(e,t){var a=c.get.mode(),n=t||"minute"===a||f.disableMinute&&"hour"===a||"date"===f.type&&"day"===a||"month"===f.type&&"month"===a||"year"===f.type&&"year"===a;if(n){var o=c.set.date(e)===!1;if(!o&&f.closable){c.popup("hide");var r=c.get.calendarModule(f.endCalendar);r&&(r.popup("show"),r.focus())}}else{var i="year"===a?f.disableMonth?"day":"month":"month"===a?"day":"day"===a?"hour":"minute";c.set.mode(i),"hour"===a||"day"===a&&c.get.date()?c.set.date(e):c.set.focusDate(e)}},changeDate:function(e){c.set.date(e)},clear:function(){c.set.date(n)},popup:function(){return T.popup.apply(T,arguments)},focus:function(){x.length?x.focus():k.focus()},blur:function(){x.length?x.blur():k.blur()},helper:{sanitiseDate:function(e){return e?(e instanceof Date||(e=y.date(""+e,f)),isNaN(e.getTime())?n:e):n},dateDiff:function(e,t,a){a=a||"day";var n="time"===f.type,o="year"===a,r=o||"month"===a,i="minute"===a,l=i||"hour"===a;return e=new Date(n?2e3:e.getFullYear(),n?0:o?0:e.getMonth(),n?1:r?1:e.getDate(),l?e.getHours():0,i?5*Math.floor(e.getMinutes()/5):0),t=new Date(n?2e3:t.getFullYear(),n?0:o?0:t.getMonth(),n?1:r?1:t.getDate(),l?t.getHours():0,i?5*Math.floor(t.getMinutes()/5):0),t.getTime()-e.getTime()},dateEqual:function(e,t,a){return!!e&&!!t&&0===c.helper.dateDiff(e,t,a)},isDateInRange:function(e,t,a,n){if(!a&&!n){var o=c.get.startDate();a=o&&f.minDate?new Date(Math.max(o,f.minDate)):o||f.minDate,n=f.maxDate}return a=a&&new Date(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours(),5*Math.ceil(a.getMinutes()/5)),!(!e||a&&c.helper.dateDiff(e,a,t)>0||n&&c.helper.dateDiff(n,e,t)>0)},dateInRange:function(e,t,a){if(!t&&!a){var n=c.get.startDate();t=n&&f.minDate?new Date(Math.max(n,f.minDate)):n||f.minDate,a=f.maxDate}t=t&&new Date(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),5*Math.ceil(t.getMinutes()/5));var o="time"===f.type;return e?t&&c.helper.dateDiff(e,t,"minute")>0?o?c.helper.mergeDateTime(e,t):t:a&&c.helper.dateDiff(a,e,"minute")>0?o?c.helper.mergeDateTime(e,a):a:e:e},mergeDateTime:function(e,t){return e&&t?new Date(e.getFullYear(),e.getMonth(),e.getDate(),t.getHours(),t.getMinutes()):t}},setting:function(t,a){if(c.debug("Changing setting",t,a),e.isPlainObject(t))e.extend(!0,f,t);else{if(a===n)return f[t];e.isPlainObject(f[t])?e.extend(!0,f[t],a):f[t]=a}},internal:function(t,a){return c.debug("Changing internal",t,a),a===n?c[t]:void(e.isPlainObject(t)?e.extend(!0,c,t):c[t]=a)},debug:function(){!f.silent&&f.debug&&(f.performance?c.performance.log(arguments):(c.debug=Function.prototype.bind.call(console.info,console,f.name+":"),c.debug.apply(console,arguments)))},verbose:function(){!f.silent&&f.verbose&&f.debug&&(f.performance?c.performance.log(arguments):(c.verbose=Function.prototype.bind.call(console.info,console,f.name+":"),c.verbose.apply(console,arguments)))},error:function(){f.silent||(c.error=Function.prototype.bind.call(console.error,console,f.name+":"),c.error.apply(console,arguments))},performance:{log:function(e){var t,a,n;f.performance&&(t=(new Date).getTime(),n=l||t,a=t-n,l=t,d.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:F,"Execution Time":a})),clearTimeout(c.performance.timer),c.performance.timer=setTimeout(c.performance.display,500)},display:function(){var t=f.name+":",a=0;l=!1,clearTimeout(c.performance.timer),e.each(d,function(e,t){a+=t["Execution Time"]}),t+=" "+a+"ms",i&&(t+=" '"+i+"'"),(console.group!==n||console.table!==n)&&d.length>0&&(console.groupCollapsed(t),console.table?console.table(d):e.each(d,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),d=[]}},invoke:function(t,a,r){var i,l,d,s=H;return a=a||p,r=F||r,"string"==typeof t&&s!==n&&(t=t.split(/[\. ]/),i=t.length-1,e.each(t,function(a,o){var r=a!=i?o+t[a+1].charAt(0).toUpperCase()+t[a+1].slice(1):t;if(e.isPlainObject(s[r])&&a!=i)s=s[r];else{if(s[r]!==n)return l=s[r],!1;if(!e.isPlainObject(s[o])||a==i)return s[o]!==n?(l=s[o],!1):(c.error(b.method,t),!1);s=s[o]}})),e.isFunction(l)?d=l.apply(r,a):l!==n&&(d=l),e.isArray(o)?o.push(d):o!==n?o=[o,d]:d!==n&&(o=d),l}},u?(H===n&&c.initialize(),c.invoke(s)):(H!==n&&H.invoke("destroy"),c.initialize())}),o!==n?o:this},e.fn.calendar.settings={name:"Calendar",namespace:"calendar",silent:!1,debug:!1,verbose:!1,performance:!1,type:"datetime",firstDayOfWeek:0,constantHeight:!0,today:!1,closable:!0,monthFirst:!0,touchReadonly:!0,inline:!1,on:null,initialDate:null,startMode:!1,minDate:null,maxDate:null,ampm:!0,disableYear:!1,disableMonth:!1,disableMinute:!1,formatInput:!0,startCalendar:null,endCalendar:null,multiMonth:1,popupOptions:{position:"bottom left",lastResort:"bottom left",prefer:"opposite",hideOnScroll:!1},text:{days:["S","M","T","W","T","F","S"],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"],today:"Today",now:"Now",am:"AM",pm:"PM"},formatter:{header:function(e,t,a){return"year"===t?a.formatter.yearHeader(e,a):"month"===t?a.formatter.monthHeader(e,a):"day"===t?a.formatter.dayHeader(e,a):"hour"===t?a.formatter.hourHeader(e,a):a.formatter.minuteHeader(e,a)},yearHeader:function(e,t){var a=10*Math.ceil(e.getFullYear()/10);return a-9+" - "+(a+2)},monthHeader:function(e,t){return e.getFullYear()},dayHeader:function(e,t){var a=t.text.months[e.getMonth()],n=e.getFullYear();return a+" "+n},hourHeader:function(e,t){return t.formatter.date(e,t)},minuteHeader:function(e,t){return t.formatter.date(e,t)},dayColumnHeader:function(e,t){return t.text.days[e]},datetime:function(e,t){if(!e)return"";var a="time"===t.type?"":t.formatter.date(e,t),n=t.type.indexOf("time")<0?"":t.formatter.time(e,t,!1),o="datetime"===t.type?" ":"";return a+o+n},date:function(e,t){if(!e)return"";var a=e.getDate(),n=t.text.months[e.getMonth()],o=e.getFullYear();return"year"===t.type?o:"month"===t.type?n+" "+o:(t.monthFirst?n+" "+a:a+" "+n)+", "+o},time:function(e,t,a){if(!e)return"";var n=e.getHours(),o=e.getMinutes(),r="";return t.ampm&&(r=" "+(n<12?t.text.am:t.text.pm),n=0===n?12:n>12?n-12:n),n+":"+(o<10?"0":"")+o+r},today:function(e){return"date"===e.type?e.text.today:e.text.now},cell:function(e,t,a){}},parser:{date:function(t,a){if(!t)return null;if(t=(""+t).trim().toLowerCase(),0===t.length)return null;var o,r,i,l=-1,d=-1,s=-1,u=-1,p=-1,c=n,f="time"===a.type,h=a.type.indexOf("time")<0,g=t.split(a.regExp.dateWords),m=t.split(a.regExp.dateNumbers);if(!h)for(c=e.inArray(a.text.am.toLowerCase(),g)>=0||!(e.inArray(a.text.pm.toLowerCase(),g)>=0)&&n,o=0;o=0){if(d<0||l<0){var y=v.split(":");for(i=0;i=0)break}}for(o=0;o59){p=r,m.splice(o,1);break}if(u<0)for(o=0;o1||a.monthFirst?o:1===o?0:1,r=parseInt(m[i]),!isNaN(r)&&1<=r&&r<=12){u=r,m.splice(i,1);break}for(o=0;o=0;o--)if(r=parseInt(m[o]),!isNaN(r)){r<99&&(r+=2e3),p=r,m.splice(o,1);break}}if(!h){if(d<0)for(o=0;o').addClass(className.popup).prependTo($activator.parent()); 117 | } 118 | $container.addClass(className.calendar); 119 | var onVisible = settings.onVisible; 120 | var onHidden = settings.onHidden; 121 | if (!$input.length) { 122 | //no input, $container has to handle focus/blur 123 | $container.attr('tabindex', '0'); 124 | onVisible = function () { 125 | module.focus(); 126 | return settings.onVisible.apply($container, arguments); 127 | }; 128 | onHidden = function () { 129 | module.blur(); 130 | return settings.onHidden.apply($container, arguments); 131 | }; 132 | } 133 | var onShow = function () { 134 | //reset the focus date onShow 135 | module.set.focusDate(module.get.date()); 136 | module.set.mode(settings.startMode); 137 | return settings.onShow.apply($container, arguments); 138 | }; 139 | var on = settings.on || ($input.length ? 'focus' : 'click'); 140 | var options = $.extend({}, settings.popupOptions, { 141 | popup: $container, 142 | on: on, 143 | hoverable: on === 'hover', 144 | onShow: onShow, 145 | onVisible: onVisible, 146 | onHide: settings.onHide, 147 | onHidden: onHidden 148 | }); 149 | module.popup(options); 150 | }, 151 | inline: function () { 152 | if ($activator.length && !settings.inline) { 153 | return; 154 | } 155 | $container = $('
').addClass(className.calendar).appendTo($module); 156 | if (!$input.length) { 157 | $container.attr('tabindex', '0'); 158 | } 159 | }, 160 | input: function () { 161 | if (settings.touchReadonly && $input.length && isTouch) { 162 | $input.prop('readonly', true); 163 | } 164 | }, 165 | date: function () { 166 | if ($input.length) { 167 | var val = $input.val(); 168 | var date = parser.date(val, settings); 169 | module.set.date(date, settings.formatInput, false); 170 | } 171 | } 172 | }, 173 | 174 | create: { 175 | calendar: function () { 176 | var i, r, c, p, row, cell, pageGrid; 177 | 178 | var mode = module.get.mode(); 179 | var today = new Date(); 180 | var date = module.get.date(); 181 | var focusDate = module.get.focusDate(); 182 | var display = focusDate || date || settings.initialDate || today; 183 | display = module.helper.dateInRange(display); 184 | 185 | if (!focusDate) { 186 | focusDate = display; 187 | module.set.focusDate(focusDate, false, false); 188 | } 189 | 190 | var isYear = mode === 'year'; 191 | var isMonth = mode === 'month'; 192 | var isDay = mode === 'day'; 193 | var isHour = mode === 'hour'; 194 | var isMinute = mode === 'minute'; 195 | var isTimeOnly = settings.type === 'time'; 196 | 197 | var multiMonth = Math.max(settings.multiMonth, 1); 198 | var monthOffset = !isDay ? 0 : module.get.monthOffset(); 199 | 200 | var minute = display.getMinutes(); 201 | var hour = display.getHours(); 202 | var day = display.getDate(); 203 | var startMonth = display.getMonth() + monthOffset; 204 | var year = display.getFullYear(); 205 | 206 | var columns = isDay ? 7 : isHour ? 4 : 3; 207 | var columnsString = columns === 7 ? 'seven' : columns === 4 ? 'four' : 'three'; 208 | var rows = isDay || isHour ? 6 : 4; 209 | var pages = isDay ? multiMonth : 1; 210 | 211 | var container = $container; 212 | container.empty(); 213 | if (pages > 1) { 214 | pageGrid = $('
').addClass(className.grid).appendTo(container); 215 | } 216 | 217 | for (p = 0; p < pages; p++) { 218 | if (pages > 1) { 219 | var pageColumn = $('
').addClass(className.column).appendTo(pageGrid); 220 | container = pageColumn; 221 | } 222 | 223 | var month = startMonth + p; 224 | var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7; 225 | if (!settings.constantHeight && isDay) { 226 | var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn; 227 | rows = Math.ceil(requiredCells / 7); 228 | } 229 | 230 | var yearChange = isYear ? 10 : isMonth ? 1 : 0; 231 | var monthChange = isDay ? 1 : 0; 232 | var dayChange = isHour || isMinute ? 1 : 0; 233 | var prevNextDay = isHour || isMinute ? day : 1; 234 | var prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour); 235 | var nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour); 236 | 237 | var prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) : 238 | isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1); 239 | var nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) : 240 | isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1); 241 | 242 | var table = $('').addClass(className.table).addClass(columnsString + ' column').addClass(mode).appendTo(container); 243 | 244 | //no header for time-only mode 245 | if (!isTimeOnly) { 246 | var thead = $('').appendTo(table); 247 | 248 | row = $('').appendTo(thead); 249 | cell = $('').appendTo(thead); 275 | for (i = 0; i < columns; i++) { 276 | cell = $('').appendTo(table); 283 | i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0; 284 | for (r = 0; r < rows; r++) { 285 | row = $('').appendTo(tbody); 286 | for (c = 0; c < columns; c++, i++) { 287 | var cellDate = isYear ? new Date(i, month, 1, hour, minute) : 288 | isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) : 289 | isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * 5); 290 | var cellText = isYear ? i : 291 | isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() : 292 | formatter.time(cellDate, settings, true); 293 | cell = $('').appendTo(tbody); 327 | var todayButton = $('
').attr('colspan', '' + columns).appendTo(row); 250 | 251 | var headerDate = isYear || isMonth ? new Date(year, 0, 1) : 252 | isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute); 253 | var headerText = $('').addClass(className.link).appendTo(cell); 254 | headerText.text(formatter.header(headerDate, mode, settings)); 255 | var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') : 256 | isDay ? (settings.disableMonth ? 'year' : 'month') : 'day'; 257 | headerText.data(metadata.mode, newMode); 258 | 259 | if (p === 0) { 260 | var prev = $('').addClass(className.prev).appendTo(cell); 261 | prev.data(metadata.focusDate, prevDate); 262 | prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode)); 263 | $('').addClass(className.prevIcon).appendTo(prev); 264 | } 265 | 266 | if (p === pages - 1) { 267 | var next = $('').addClass(className.next).appendTo(cell); 268 | next.data(metadata.focusDate, nextDate); 269 | next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode)); 270 | $('').addClass(className.nextIcon).appendTo(next); 271 | } 272 | 273 | if (isDay) { 274 | row = $('
').appendTo(row); 277 | cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings)); 278 | } 279 | } 280 | } 281 | 282 | var tbody = $('
').addClass(className.cell).appendTo(row); 294 | cell.text(cellText); 295 | cell.data(metadata.date, cellDate); 296 | var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12); 297 | var disabled = adjacent || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode); 298 | var active = module.helper.dateEqual(cellDate, date, mode); 299 | var isToday = module.helper.dateEqual(cellDate, today, mode); 300 | cell.toggleClass(className.adjacentCell, adjacent); 301 | cell.toggleClass(className.disabledCell, disabled); 302 | cell.toggleClass(className.activeCell, active && !adjacent); 303 | if (!isHour && !isMinute) { 304 | cell.toggleClass(className.todayCell, !adjacent && isToday); 305 | } 306 | 307 | // Allow for external modifications of each cell 308 | var cellOptions = { 309 | mode: mode, 310 | adjacent: adjacent, 311 | disabled: disabled, 312 | active: active, 313 | today: isToday 314 | }; 315 | formatter.cell(cell, cellDate, cellOptions); 316 | 317 | if (module.helper.dateEqual(cellDate, focusDate, mode)) { 318 | //ensure that the focus date is exactly equal to the cell date 319 | //so that, if selected, the correct value is set 320 | module.set.focusDate(cellDate, false, false); 321 | } 322 | } 323 | } 324 | 325 | if (settings.today) { 326 | var todayRow = $('
').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow); 328 | todayButton.text(formatter.today(settings)); 329 | todayButton.data(metadata.date, today); 330 | } 331 | 332 | module.update.focus(false, table); 333 | } 334 | } 335 | }, 336 | 337 | update: { 338 | focus: function (updateRange, container) { 339 | container = container || $container; 340 | var mode = module.get.mode(); 341 | var date = module.get.date(); 342 | var focusDate = module.get.focusDate(); 343 | var startDate = module.get.startDate(); 344 | var endDate = module.get.endDate(); 345 | var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null); 346 | 347 | container.find('td').each(function () { 348 | var cell = $(this); 349 | var cellDate = cell.data(metadata.date); 350 | if (!cellDate) { 351 | return; 352 | } 353 | var disabled = cell.hasClass(className.disabledCell); 354 | var active = cell.hasClass(className.activeCell); 355 | var adjacent = cell.hasClass(className.adjacentCell); 356 | var focused = module.helper.dateEqual(cellDate, focusDate, mode); 357 | var inRange = !rangeDate ? false : 358 | ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) || 359 | (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate))); 360 | cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && !adjacent); 361 | cell.toggleClass(className.rangeCell, inRange && !active && !disabled); 362 | }); 363 | } 364 | }, 365 | 366 | refresh: function () { 367 | module.create.calendar(); 368 | }, 369 | 370 | bind: { 371 | events: function () { 372 | $container.on('mousedown' + eventNamespace, module.event.mousedown); 373 | $container.on('touchstart' + eventNamespace, module.event.mousedown); 374 | $container.on('mouseup' + eventNamespace, module.event.mouseup); 375 | $container.on('touchend' + eventNamespace, module.event.mouseup); 376 | $container.on('mouseover' + eventNamespace, module.event.mouseover); 377 | if ($input.length) { 378 | $input.on('input' + eventNamespace, module.event.inputChange); 379 | $input.on('focus' + eventNamespace, module.event.inputFocus); 380 | $input.on('blur' + eventNamespace, module.event.inputBlur); 381 | $input.on('click' + eventNamespace, module.event.inputClick); 382 | $input.on('keydown' + eventNamespace, module.event.keydown); 383 | } else { 384 | $container.on('keydown' + eventNamespace, module.event.keydown); 385 | } 386 | } 387 | }, 388 | 389 | unbind: { 390 | events: function () { 391 | $container.off(eventNamespace); 392 | if ($input.length) { 393 | $input.off(eventNamespace); 394 | } 395 | } 396 | }, 397 | 398 | event: { 399 | mouseover: function (event) { 400 | var target = $(event.target); 401 | var date = target.data(metadata.date); 402 | var mousedown = event.buttons === 1; 403 | if (date) { 404 | module.set.focusDate(date, false, true, mousedown); 405 | } 406 | }, 407 | mousedown: function (event) { 408 | if ($input.length) { 409 | //prevent the mousedown on the calendar causing the input to lose focus 410 | event.preventDefault(); 411 | } 412 | isTouchDown = event.type.indexOf('touch') >= 0; 413 | var target = $(event.target); 414 | var date = target.data(metadata.date); 415 | if (date) { 416 | module.set.focusDate(date, false, true, true); 417 | } 418 | }, 419 | mouseup: function (event) { 420 | //ensure input has focus so that it receives keydown events for calendar navigation 421 | module.focus(); 422 | event.preventDefault(); 423 | event.stopPropagation(); 424 | isTouchDown = false; 425 | var target = $(event.target); 426 | var parent = target.parent(); 427 | if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) { 428 | //clicked on a child element, switch to parent (used when clicking directly on prev/next icon element) 429 | target = parent; 430 | } 431 | var date = target.data(metadata.date); 432 | var focusDate = target.data(metadata.focusDate); 433 | var mode = target.data(metadata.mode); 434 | if (date) { 435 | var forceSet = target.hasClass(className.today); 436 | module.selectDate(date, forceSet); 437 | } 438 | else if (focusDate) { 439 | module.set.focusDate(focusDate); 440 | } 441 | else if (mode) { 442 | module.set.mode(mode); 443 | } 444 | }, 445 | keydown: function (event) { 446 | if (event.keyCode === 27 || event.keyCode === 9) { 447 | //esc || tab 448 | module.popup('hide'); 449 | } 450 | 451 | if (module.popup('is visible')) { 452 | if (event.keyCode === 37 || event.keyCode === 38 || event.keyCode === 39 || event.keyCode === 40) { 453 | //arrow keys 454 | var mode = module.get.mode(); 455 | var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : 3; 456 | var increment = event.keyCode === 37 ? -1 : event.keyCode === 38 ? -bigIncrement : event.keyCode == 39 ? 1 : bigIncrement; 457 | increment *= mode === 'minute' ? 5 : 1; 458 | var focusDate = module.get.focusDate() || module.get.date() || new Date(); 459 | var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0); 460 | var month = focusDate.getMonth() + (mode === 'month' ? increment : 0); 461 | var day = focusDate.getDate() + (mode === 'day' ? increment : 0); 462 | var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0); 463 | var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0); 464 | var newFocusDate = new Date(year, month, day, hour, minute); 465 | if (settings.type === 'time') { 466 | newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate); 467 | } 468 | if (module.helper.isDateInRange(newFocusDate, mode)) { 469 | module.set.focusDate(newFocusDate); 470 | } 471 | } else if (event.keyCode === 13) { 472 | //enter 473 | var mode = module.get.mode(); 474 | var date = module.get.focusDate(); 475 | if (date && !settings.isDisabled(date, mode)) { 476 | module.selectDate(date); 477 | } 478 | //disable form submission: 479 | event.preventDefault(); 480 | event.stopPropagation(); 481 | } 482 | } 483 | 484 | if (event.keyCode === 38 || event.keyCode === 40) { 485 | //arrow-up || arrow-down 486 | event.preventDefault(); //don't scroll 487 | module.popup('show'); 488 | } 489 | }, 490 | inputChange: function () { 491 | var val = $input.val(); 492 | var date = parser.date(val, settings); 493 | module.set.date(date, false); 494 | }, 495 | inputFocus: function () { 496 | $container.addClass(className.active); 497 | }, 498 | inputBlur: function () { 499 | $container.removeClass(className.active); 500 | if (settings.formatInput) { 501 | var date = module.get.date(); 502 | var text = formatter.datetime(date, settings); 503 | $input.val(text); 504 | } 505 | }, 506 | inputClick: function () { 507 | module.popup('show'); 508 | } 509 | }, 510 | 511 | get: { 512 | date: function () { 513 | return $module.data(metadata.date) || null; 514 | }, 515 | focusDate: function () { 516 | return $module.data(metadata.focusDate) || null; 517 | }, 518 | startDate: function () { 519 | var startModule = module.get.calendarModule(settings.startCalendar); 520 | return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null; 521 | }, 522 | endDate: function () { 523 | var endModule = module.get.calendarModule(settings.endCalendar); 524 | return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null; 525 | }, 526 | monthOffset: function () { 527 | return $module.data(metadata.monthOffset) || 0; 528 | }, 529 | mode: function () { 530 | //only returns valid modes for the current settings 531 | var mode = $module.data(metadata.mode) || settings.startMode; 532 | var validModes = module.get.validModes(); 533 | if ($.inArray(mode, validModes) >= 0) { 534 | return mode; 535 | } 536 | return settings.type === 'time' ? 'hour' : 537 | settings.type === 'month' ? 'month' : 538 | settings.type === 'year' ? 'year' : 'day'; 539 | }, 540 | validModes: function () { 541 | var validModes = []; 542 | if (settings.type !== 'time') { 543 | if (!settings.disableYear || settings.type === 'year') { 544 | validModes.push('year'); 545 | } 546 | if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') { 547 | validModes.push('month'); 548 | } 549 | if (settings.type.indexOf('date') >= 0) { 550 | validModes.push('day'); 551 | } 552 | } 553 | if (settings.type.indexOf('time') >= 0) { 554 | validModes.push('hour'); 555 | if (!settings.disableMinute) { 556 | validModes.push('minute'); 557 | } 558 | } 559 | return validModes; 560 | }, 561 | isTouch: function () { 562 | try { 563 | document.createEvent('TouchEvent'); 564 | return true; 565 | } 566 | catch (e) { 567 | return false; 568 | } 569 | }, 570 | calendarModule: function (selector) { 571 | if (!selector) { 572 | return null; 573 | } 574 | if (!(selector instanceof $)) { 575 | selector = $module.parent().children(selector).first(); 576 | } 577 | //assume range related calendars are using the same namespace 578 | return selector.data(moduleNamespace); 579 | } 580 | }, 581 | 582 | set: { 583 | date: function (date, updateInput, fireChange) { 584 | updateInput = updateInput !== false; 585 | fireChange = fireChange !== false; 586 | date = module.helper.sanitiseDate(date); 587 | date = module.helper.dateInRange(date); 588 | 589 | var mode = module.get.mode(); 590 | var text = formatter.datetime(date, settings); 591 | if (fireChange && settings.onChange.call(element, date, text, mode) === false) { 592 | return false; 593 | } 594 | 595 | module.set.focusDate(date); 596 | 597 | if (settings.isDisabled(date, mode)) { 598 | return false; 599 | } 600 | 601 | var endDate = module.get.endDate(); 602 | if (!!endDate && !!date && date > endDate) { 603 | //selected date is greater than end date in range, so clear end date 604 | module.set.endDate(undefined); 605 | } 606 | module.set.dataKeyValue(metadata.date, date); 607 | 608 | if (updateInput && $input.length) { 609 | $input.val(text); 610 | } 611 | }, 612 | startDate: function (date, refreshCalendar) { 613 | date = module.helper.sanitiseDate(date); 614 | var startModule = module.get.calendarModule(settings.startCalendar); 615 | if (startModule) { 616 | startModule.set.date(date); 617 | } 618 | module.set.dataKeyValue(metadata.startDate, date, refreshCalendar); 619 | }, 620 | endDate: function (date, refreshCalendar) { 621 | date = module.helper.sanitiseDate(date); 622 | var endModule = module.get.calendarModule(settings.endCalendar); 623 | if (endModule) { 624 | endModule.set.date(date); 625 | } 626 | module.set.dataKeyValue(metadata.endDate, date, refreshCalendar); 627 | }, 628 | focusDate: function (date, refreshCalendar, updateFocus, updateRange) { 629 | date = module.helper.sanitiseDate(date); 630 | date = module.helper.dateInRange(date); 631 | var isDay = module.get.mode() === 'day'; 632 | var oldFocusDate = module.get.focusDate(); 633 | if (isDay && date && oldFocusDate) { 634 | var yearDelta = date.getFullYear() - oldFocusDate.getFullYear(); 635 | var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth(); 636 | if (monthDelta) { 637 | var monthOffset = module.get.monthOffset() - monthDelta; 638 | module.set.monthOffset(monthOffset, false); 639 | } 640 | } 641 | var changed = module.set.dataKeyValue(metadata.focusDate, date, refreshCalendar); 642 | updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange; 643 | focusDateUsedForRange = updateRange; 644 | if (updateFocus) { 645 | module.update.focus(updateRange); 646 | } 647 | }, 648 | monthOffset: function (monthOffset, refreshCalendar) { 649 | var multiMonth = Math.max(settings.multiMonth, 1); 650 | monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset)); 651 | module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar); 652 | }, 653 | mode: function (mode, refreshCalendar) { 654 | module.set.dataKeyValue(metadata.mode, mode, refreshCalendar); 655 | }, 656 | dataKeyValue: function (key, value, refreshCalendar) { 657 | var oldValue = $module.data(key); 658 | var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects 659 | if (value) { 660 | $module.data(key, value); 661 | } else { 662 | $module.removeData(key); 663 | } 664 | refreshCalendar = refreshCalendar !== false && !equal; 665 | if (refreshCalendar) { 666 | module.create.calendar(); 667 | } 668 | return !equal; 669 | } 670 | }, 671 | 672 | selectDate: function (date, forceSet) { 673 | var mode = module.get.mode(); 674 | var complete = forceSet || mode === 'minute' || 675 | (settings.disableMinute && mode === 'hour') || 676 | (settings.type === 'date' && mode === 'day') || 677 | (settings.type === 'month' && mode === 'month') || 678 | (settings.type === 'year' && mode === 'year'); 679 | if (complete) { 680 | var canceled = module.set.date(date) === false; 681 | if (!canceled && settings.closable) { 682 | module.popup('hide'); 683 | //if this is a range calendar, show the end date calendar popup and focus the input 684 | var endModule = module.get.calendarModule(settings.endCalendar); 685 | if (endModule) { 686 | endModule.popup('show'); 687 | endModule.focus(); 688 | } 689 | } 690 | } else { 691 | var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') : 692 | mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute'; 693 | module.set.mode(newMode); 694 | if (mode === 'hour' || (mode === 'day' && module.get.date())) { 695 | //the user has chosen enough to consider a valid date/time has been chosen 696 | module.set.date(date); 697 | } else { 698 | module.set.focusDate(date); 699 | } 700 | } 701 | }, 702 | 703 | changeDate: function (date) { 704 | module.set.date(date); 705 | }, 706 | 707 | clear: function () { 708 | module.set.date(undefined); 709 | }, 710 | 711 | popup: function () { 712 | return $activator.popup.apply($activator, arguments); 713 | }, 714 | 715 | focus: function () { 716 | if ($input.length) { 717 | $input.focus(); 718 | } else { 719 | $container.focus(); 720 | } 721 | }, 722 | blur: function () { 723 | if ($input.length) { 724 | $input.blur(); 725 | } else { 726 | $container.blur(); 727 | } 728 | }, 729 | 730 | helper: { 731 | sanitiseDate: function (date) { 732 | if (!date) { 733 | return undefined; 734 | } 735 | if (!(date instanceof Date)) { 736 | date = parser.date('' + date, settings); 737 | } 738 | if (isNaN(date.getTime())) { 739 | return undefined; 740 | } 741 | return date; 742 | }, 743 | dateDiff: function (date1, date2, mode) { 744 | mode = mode || 'day'; 745 | var isTimeOnly = settings.type === 'time'; 746 | var isYear = mode === 'year'; 747 | var isYearOrMonth = isYear || mode === 'month'; 748 | var isMinute = mode === 'minute'; 749 | var isHourOrMinute = isMinute || mode === 'hour'; 750 | //only care about a minute accuracy of 5 751 | date1 = new Date( 752 | isTimeOnly ? 2000 : date1.getFullYear(), 753 | isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(), 754 | isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(), 755 | !isHourOrMinute ? 0 : date1.getHours(), 756 | !isMinute ? 0 : 5 * Math.floor(date1.getMinutes() / 5)); 757 | date2 = new Date( 758 | isTimeOnly ? 2000 : date2.getFullYear(), 759 | isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(), 760 | isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(), 761 | !isHourOrMinute ? 0 : date2.getHours(), 762 | !isMinute ? 0 : 5 * Math.floor(date2.getMinutes() / 5)); 763 | return date2.getTime() - date1.getTime(); 764 | }, 765 | dateEqual: function (date1, date2, mode) { 766 | return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0; 767 | }, 768 | isDateInRange: function (date, mode, minDate, maxDate) { 769 | if (!minDate && !maxDate) { 770 | var startDate = module.get.startDate(); 771 | minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; 772 | maxDate = settings.maxDate; 773 | } 774 | minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), 5 * Math.ceil(minDate.getMinutes() / 5)); 775 | return !(!date || 776 | (minDate && module.helper.dateDiff(date, minDate, mode) > 0) || 777 | (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0)); 778 | }, 779 | dateInRange: function (date, minDate, maxDate) { 780 | if (!minDate && !maxDate) { 781 | var startDate = module.get.startDate(); 782 | minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; 783 | maxDate = settings.maxDate; 784 | } 785 | minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), 5 * Math.ceil(minDate.getMinutes() / 5)); 786 | var isTimeOnly = settings.type === 'time'; 787 | return !date ? date : 788 | (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ? 789 | (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) : 790 | (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ? 791 | (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) : 792 | date; 793 | }, 794 | mergeDateTime: function (date, time) { 795 | return (!date || !time) ? time : 796 | new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes()); 797 | } 798 | }, 799 | 800 | setting: function (name, value) { 801 | module.debug('Changing setting', name, value); 802 | if ($.isPlainObject(name)) { 803 | $.extend(true, settings, name); 804 | } 805 | else if (value !== undefined) { 806 | if ($.isPlainObject(settings[name])) { 807 | $.extend(true, settings[name], value); 808 | } 809 | else { 810 | settings[name] = value; 811 | } 812 | } 813 | else { 814 | return settings[name]; 815 | } 816 | }, 817 | internal: function (name, value) { 818 | module.debug('Changing internal', name, value); 819 | if (value !== undefined) { 820 | if ($.isPlainObject(name)) { 821 | $.extend(true, module, name); 822 | } 823 | else { 824 | module[name] = value; 825 | } 826 | } 827 | else { 828 | return module[name]; 829 | } 830 | }, 831 | debug: function () { 832 | if (!settings.silent && settings.debug) { 833 | if (settings.performance) { 834 | module.performance.log(arguments); 835 | } 836 | else { 837 | module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); 838 | module.debug.apply(console, arguments); 839 | } 840 | } 841 | }, 842 | verbose: function () { 843 | if (!settings.silent && settings.verbose && settings.debug) { 844 | if (settings.performance) { 845 | module.performance.log(arguments); 846 | } 847 | else { 848 | module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); 849 | module.verbose.apply(console, arguments); 850 | } 851 | } 852 | }, 853 | error: function () { 854 | if (!settings.silent) { 855 | module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); 856 | module.error.apply(console, arguments); 857 | } 858 | }, 859 | performance: { 860 | log: function (message) { 861 | var 862 | currentTime, 863 | executionTime, 864 | previousTime 865 | ; 866 | if (settings.performance) { 867 | currentTime = new Date().getTime(); 868 | previousTime = time || currentTime; 869 | executionTime = currentTime - previousTime; 870 | time = currentTime; 871 | performance.push({ 872 | 'Name': message[0], 873 | 'Arguments': [].slice.call(message, 1) || '', 874 | 'Element': element, 875 | 'Execution Time': executionTime 876 | }); 877 | } 878 | clearTimeout(module.performance.timer); 879 | module.performance.timer = setTimeout(module.performance.display, 500); 880 | }, 881 | display: function () { 882 | var 883 | title = settings.name + ':', 884 | totalTime = 0 885 | ; 886 | time = false; 887 | clearTimeout(module.performance.timer); 888 | $.each(performance, function (index, data) { 889 | totalTime += data['Execution Time']; 890 | }); 891 | title += ' ' + totalTime + 'ms'; 892 | if (moduleSelector) { 893 | title += ' \'' + moduleSelector + '\''; 894 | } 895 | if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) { 896 | console.groupCollapsed(title); 897 | if (console.table) { 898 | console.table(performance); 899 | } 900 | else { 901 | $.each(performance, function (index, data) { 902 | console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms'); 903 | }); 904 | } 905 | console.groupEnd(); 906 | } 907 | performance = []; 908 | } 909 | }, 910 | invoke: function (query, passedArguments, context) { 911 | var 912 | object = instance, 913 | maxDepth, 914 | found, 915 | response 916 | ; 917 | passedArguments = passedArguments || queryArguments; 918 | context = element || context; 919 | if (typeof query == 'string' && object !== undefined) { 920 | query = query.split(/[\. ]/); 921 | maxDepth = query.length - 1; 922 | $.each(query, function (depth, value) { 923 | var camelCaseValue = (depth != maxDepth) 924 | ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) 925 | : query 926 | ; 927 | if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) { 928 | object = object[camelCaseValue]; 929 | } 930 | else if (object[camelCaseValue] !== undefined) { 931 | found = object[camelCaseValue]; 932 | return false; 933 | } 934 | else if ($.isPlainObject(object[value]) && (depth != maxDepth)) { 935 | object = object[value]; 936 | } 937 | else if (object[value] !== undefined) { 938 | found = object[value]; 939 | return false; 940 | } 941 | else { 942 | module.error(error.method, query); 943 | return false; 944 | } 945 | }); 946 | } 947 | if ($.isFunction(found)) { 948 | response = found.apply(context, passedArguments); 949 | } 950 | else if (found !== undefined) { 951 | response = found; 952 | } 953 | if ($.isArray(returnedValue)) { 954 | returnedValue.push(response); 955 | } 956 | else if (returnedValue !== undefined) { 957 | returnedValue = [returnedValue, response]; 958 | } 959 | else if (response !== undefined) { 960 | returnedValue = response; 961 | } 962 | return found; 963 | } 964 | }; 965 | 966 | if (methodInvoked) { 967 | if (instance === undefined) { 968 | module.initialize(); 969 | } 970 | module.invoke(query); 971 | } 972 | else { 973 | if (instance !== undefined) { 974 | instance.invoke('destroy'); 975 | } 976 | module.initialize(); 977 | } 978 | }) 979 | ; 980 | return (returnedValue !== undefined) 981 | ? returnedValue 982 | : this 983 | ; 984 | }; 985 | 986 | $.fn.calendar.settings = { 987 | 988 | name: 'Calendar', 989 | namespace: 'calendar', 990 | 991 | silent: false, 992 | debug: false, 993 | verbose: false, 994 | performance: false, 995 | 996 | type: 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year' 997 | firstDayOfWeek: 0, // day for first day column (0 = Sunday) 998 | constantHeight: true, // add rows to shorter months to keep day calendar height consistent (6 rows) 999 | today: false, // show a 'today/now' button at the bottom of the calendar 1000 | closable: true, // close the popup after selecting a date/time 1001 | monthFirst: true, // month before day when parsing/converting date from/to text 1002 | touchReadonly: true, // set input to readonly on touch devices 1003 | inline: false, // create the calendar inline instead of inside a popup 1004 | on: null, // when to show the popup (defaults to 'focus' for input, 'click' for others) 1005 | initialDate: null, // date to display initially when no date is selected (null = now) 1006 | startMode: false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day') 1007 | minDate: null, // minimum date/time that can be selected, dates/times before are disabled 1008 | maxDate: null, // maximum date/time that can be selected, dates/times after are disabled 1009 | ampm: true, // show am/pm in time mode 1010 | disableYear: false, // disable year selection mode 1011 | disableMonth: false, // disable month selection mode 1012 | disableMinute: false, // disable minute selection mode 1013 | formatInput: true, // format the input text upon input blur and module creation 1014 | startCalendar: null, // jquery object or selector for another calendar that represents the start date of a date range 1015 | endCalendar: null, // jquery object or selector for another calendar that represents the end date of a date range 1016 | multiMonth: 1, // show multiple months when in 'day' mode 1017 | 1018 | // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden) 1019 | popupOptions: { 1020 | position: 'bottom left', 1021 | lastResort: 'bottom left', 1022 | prefer: 'opposite', 1023 | hideOnScroll: false 1024 | }, 1025 | 1026 | text: { 1027 | days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 1028 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 1029 | monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 1030 | today: 'Today', 1031 | now: 'Now', 1032 | am: 'AM', 1033 | pm: 'PM' 1034 | }, 1035 | 1036 | formatter: { 1037 | header: function (date, mode, settings) { 1038 | return mode === 'year' ? settings.formatter.yearHeader(date, settings) : 1039 | mode === 'month' ? settings.formatter.monthHeader(date, settings) : 1040 | mode === 'day' ? settings.formatter.dayHeader(date, settings) : 1041 | mode === 'hour' ? settings.formatter.hourHeader(date, settings) : 1042 | settings.formatter.minuteHeader(date, settings); 1043 | }, 1044 | yearHeader: function (date, settings) { 1045 | var decadeYear = Math.ceil(date.getFullYear() / 10) * 10; 1046 | return (decadeYear - 9) + ' - ' + (decadeYear + 2); 1047 | }, 1048 | monthHeader: function (date, settings) { 1049 | return date.getFullYear(); 1050 | }, 1051 | dayHeader: function (date, settings) { 1052 | var month = settings.text.months[date.getMonth()]; 1053 | var year = date.getFullYear(); 1054 | return month + ' ' + year; 1055 | }, 1056 | hourHeader: function (date, settings) { 1057 | return settings.formatter.date(date, settings); 1058 | }, 1059 | minuteHeader: function (date, settings) { 1060 | return settings.formatter.date(date, settings); 1061 | }, 1062 | dayColumnHeader: function (day, settings) { 1063 | return settings.text.days[day]; 1064 | }, 1065 | datetime: function (date, settings) { 1066 | if (!date) { 1067 | return ''; 1068 | } 1069 | var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings); 1070 | var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false); 1071 | var separator = settings.type === 'datetime' ? ' ' : ''; 1072 | return day + separator + time; 1073 | }, 1074 | date: function (date, settings) { 1075 | if (!date) { 1076 | return ''; 1077 | } 1078 | var day = date.getDate(); 1079 | var month = settings.text.months[date.getMonth()]; 1080 | var year = date.getFullYear(); 1081 | return settings.type === 'year' ? year : 1082 | settings.type === 'month' ? month + ' ' + year : 1083 | (settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year; 1084 | }, 1085 | time: function (date, settings, forCalendar) { 1086 | if (!date) { 1087 | return ''; 1088 | } 1089 | var hour = date.getHours(); 1090 | var minute = date.getMinutes(); 1091 | var ampm = ''; 1092 | if (settings.ampm) { 1093 | ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm); 1094 | hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; 1095 | } 1096 | return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm; 1097 | }, 1098 | today: function (settings) { 1099 | return settings.type === 'date' ? settings.text.today : settings.text.now; 1100 | }, 1101 | cell: function (cell, date, cellOptions) { 1102 | } 1103 | }, 1104 | 1105 | parser: { 1106 | date: function (text, settings) { 1107 | if (!text) { 1108 | return null; 1109 | } 1110 | text = ('' + text).trim().toLowerCase(); 1111 | if (text.length === 0) { 1112 | return null; 1113 | } 1114 | 1115 | var i, j, k; 1116 | var minute = -1, hour = -1, day = -1, month = -1, year = -1; 1117 | var isAm = undefined; 1118 | 1119 | var isTimeOnly = settings.type === 'time'; 1120 | var isDateOnly = settings.type.indexOf('time') < 0; 1121 | 1122 | var words = text.split(settings.regExp.dateWords); 1123 | var numbers = text.split(settings.regExp.dateNumbers); 1124 | 1125 | if (!isDateOnly) { 1126 | //am/pm 1127 | isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true : 1128 | $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined; 1129 | 1130 | //time with ':' 1131 | for (i = 0; i < numbers.length; i++) { 1132 | var number = numbers[i]; 1133 | if (number.indexOf(':') >= 0) { 1134 | if (hour < 0 || minute < 0) { 1135 | var parts = number.split(':'); 1136 | for (k = 0; k < Math.min(2, parts.length); k++) { 1137 | j = parseInt(parts[k]); 1138 | if (isNaN(j)) { 1139 | j = 0; 1140 | } 1141 | if (k === 0) { 1142 | hour = j % 24; 1143 | } else { 1144 | minute = j % 60; 1145 | } 1146 | } 1147 | } 1148 | numbers.splice(i, 1); 1149 | } 1150 | } 1151 | } 1152 | 1153 | if (!isTimeOnly) { 1154 | //textual month 1155 | for (i = 0; i < words.length; i++) { 1156 | var word = words[i]; 1157 | if (word.length <= 0) { 1158 | continue; 1159 | } 1160 | word = word.substring(0, Math.min(word.length, 3)); 1161 | for (j = 0; j < settings.text.months.length; j++) { 1162 | var monthString = settings.text.months[j]; 1163 | monthString = monthString.substring(0, Math.min(word.length, Math.min(monthString.length, 3))).toLowerCase(); 1164 | if (monthString === word) { 1165 | month = j + 1; 1166 | break; 1167 | } 1168 | } 1169 | if (month >= 0) { 1170 | break; 1171 | } 1172 | } 1173 | 1174 | //year > 59 1175 | for (i = 0; i < numbers.length; i++) { 1176 | j = parseInt(numbers[i]); 1177 | if (isNaN(j)) { 1178 | continue; 1179 | } 1180 | if (j > 59) { 1181 | year = j; 1182 | numbers.splice(i, 1); 1183 | break; 1184 | } 1185 | } 1186 | 1187 | //numeric month 1188 | if (month < 0) { 1189 | for (i = 0; i < numbers.length; i++) { 1190 | k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1; 1191 | j = parseInt(numbers[k]); 1192 | if (isNaN(j)) { 1193 | continue; 1194 | } 1195 | if (1 <= j && j <= 12) { 1196 | month = j; 1197 | numbers.splice(k, 1); 1198 | break; 1199 | } 1200 | } 1201 | } 1202 | 1203 | //day 1204 | for (i = 0; i < numbers.length; i++) { 1205 | j = parseInt(numbers[i]); 1206 | if (isNaN(j)) { 1207 | continue; 1208 | } 1209 | if (1 <= j && j <= 31) { 1210 | day = j; 1211 | numbers.splice(i, 1); 1212 | break; 1213 | } 1214 | } 1215 | 1216 | //year <= 59 1217 | if (year < 0) { 1218 | for (i = numbers.length - 1; i >= 0; i--) { 1219 | j = parseInt(numbers[i]); 1220 | if (isNaN(j)) { 1221 | continue; 1222 | } 1223 | if (j < 99) { 1224 | j += 2000; 1225 | } 1226 | year = j; 1227 | numbers.splice(i, 1); 1228 | break; 1229 | } 1230 | } 1231 | } 1232 | 1233 | if (!isDateOnly) { 1234 | //hour 1235 | if (hour < 0) { 1236 | for (i = 0; i < numbers.length; i++) { 1237 | j = parseInt(numbers[i]); 1238 | if (isNaN(j)) { 1239 | continue; 1240 | } 1241 | if (0 <= j && j <= 23) { 1242 | hour = j; 1243 | numbers.splice(i, 1); 1244 | break; 1245 | } 1246 | } 1247 | } 1248 | 1249 | //minute 1250 | if (minute < 0) { 1251 | for (i = 0; i < numbers.length; i++) { 1252 | j = parseInt(numbers[i]); 1253 | if (isNaN(j)) { 1254 | continue; 1255 | } 1256 | if (0 <= j && j <= 59) { 1257 | minute = j; 1258 | numbers.splice(i, 1); 1259 | break; 1260 | } 1261 | } 1262 | } 1263 | } 1264 | 1265 | if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) { 1266 | return null; 1267 | } 1268 | 1269 | if (minute < 0) { 1270 | minute = 0; 1271 | } 1272 | if (hour < 0) { 1273 | hour = 0; 1274 | } 1275 | if (day < 0) { 1276 | day = 1; 1277 | } 1278 | if (month < 0) { 1279 | month = 1; 1280 | } 1281 | if (year < 0) { 1282 | year = new Date().getFullYear(); 1283 | } 1284 | 1285 | if (isAm !== undefined) { 1286 | if (isAm) { 1287 | if (hour === 12) { 1288 | hour = 0; 1289 | } 1290 | } else if (hour < 12) { 1291 | hour += 12; 1292 | } 1293 | } 1294 | 1295 | var date = new Date(year, month - 1, day, hour, minute); 1296 | if (date.getMonth() !== month - 1 || date.getFullYear() !== year) { 1297 | //month or year don't match up, switch to last day of the month 1298 | date = new Date(year, month, 0, hour, minute); 1299 | } 1300 | return isNaN(date.getTime()) ? null : date; 1301 | } 1302 | }, 1303 | 1304 | // callback when date changes, return false to cancel the change 1305 | onChange: function (date, text, mode) { 1306 | return true; 1307 | }, 1308 | 1309 | // callback before show animation, return false to prevent show 1310 | onShow: function () { 1311 | }, 1312 | 1313 | // callback after show animation 1314 | onVisible: function () { 1315 | }, 1316 | 1317 | // callback before hide animation, return false to prevent hide 1318 | onHide: function () { 1319 | }, 1320 | 1321 | // callback after hide animation 1322 | onHidden: function () { 1323 | }, 1324 | 1325 | // is the given date disabled? 1326 | isDisabled: function (date, mode) { 1327 | return false; 1328 | }, 1329 | 1330 | selector: { 1331 | popup: '.ui.popup', 1332 | input: 'input', 1333 | activator: 'input' 1334 | }, 1335 | 1336 | regExp: { 1337 | dateWords: /[^A-Za-z\u00C0-\u024F]+/g, 1338 | dateNumbers: /[^\d:]+/g 1339 | }, 1340 | 1341 | error: { 1342 | popup: 'UI Popup, a required component is not included in this page', 1343 | method: 'The method you called is not defined.' 1344 | }, 1345 | 1346 | className: { 1347 | calendar: 'calendar', 1348 | active: 'active', 1349 | popup: 'ui popup', 1350 | grid: 'ui equal width grid', 1351 | column: 'column', 1352 | table: 'ui celled center aligned unstackable table', 1353 | prev: 'prev link', 1354 | next: 'next link', 1355 | prevIcon: 'chevron left icon', 1356 | nextIcon: 'chevron right icon', 1357 | link: 'link', 1358 | cell: 'link', 1359 | disabledCell: 'disabled', 1360 | adjacentCell: 'adjacent', 1361 | activeCell: 'active', 1362 | rangeCell: 'range', 1363 | focusCell: 'focus', 1364 | todayCell: 'today', 1365 | today: 'today link' 1366 | }, 1367 | 1368 | metadata: { 1369 | date: 'date', 1370 | focusDate: 'focusDate', 1371 | startDate: 'startDate', 1372 | endDate: 'endDate', 1373 | mode: 'mode', 1374 | monthOffset: 'monthOffset' 1375 | } 1376 | }; 1377 | 1378 | })(jQuery, window, document); 1379 | -------------------------------------------------------------------------------- /src/definitions/modules/calendar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * # Semantic UI - Calendar 3 | * http://github.com/semantic-org/semantic-ui/ 4 | * 5 | * 6 | * Released under the MIT license 7 | * http://opensource.org/licenses/MIT 8 | */ 9 | 10 | ; 11 | (function ($, window, document, undefined) { 12 | 13 | "use strict"; 14 | 15 | window = (typeof window != 'undefined' && window.Math == Math) 16 | ? window 17 | : (typeof self != 'undefined' && self.Math == Math) 18 | ? self 19 | : Function('return this')() 20 | ; 21 | 22 | $.fn.calendar = function (parameters) { 23 | 24 | var 25 | $allModules = $(this), 26 | 27 | moduleSelector = $allModules.selector || '', 28 | 29 | time = new Date().getTime(), 30 | performance = [], 31 | 32 | query = arguments[0], 33 | methodInvoked = (typeof query == 'string'), 34 | queryArguments = [].slice.call(arguments, 1), 35 | returnedValue 36 | ; 37 | 38 | $allModules 39 | .each(function () { 40 | var 41 | settings = ( $.isPlainObject(parameters) ) 42 | ? $.extend(true, {}, $.fn.calendar.settings, parameters) 43 | : $.extend({}, $.fn.calendar.settings), 44 | 45 | className = settings.className, 46 | namespace = settings.namespace, 47 | selector = settings.selector, 48 | formatter = settings.formatter, 49 | parser = settings.parser, 50 | metadata = settings.metadata, 51 | error = settings.error, 52 | 53 | eventNamespace = '.' + namespace, 54 | moduleNamespace = 'module-' + namespace, 55 | 56 | $module = $(this), 57 | $input = $module.find(selector.input), 58 | $container = $module.find(selector.popup), 59 | $activator = $module.find(selector.activator), 60 | 61 | element = this, 62 | instance = $module.data(moduleNamespace), 63 | 64 | isTouch, 65 | isTouchDown = false, 66 | focusDateUsedForRange = false, 67 | module 68 | ; 69 | 70 | module = { 71 | 72 | initialize: function () { 73 | module.debug('Initializing calendar for', element); 74 | 75 | isTouch = module.get.isTouch(); 76 | module.setup.popup(); 77 | module.setup.inline(); 78 | module.setup.input(); 79 | module.setup.date(); 80 | module.create.calendar(); 81 | 82 | module.bind.events(); 83 | module.instantiate(); 84 | }, 85 | 86 | instantiate: function () { 87 | module.verbose('Storing instance of calendar'); 88 | instance = module; 89 | $module.data(moduleNamespace, instance); 90 | }, 91 | 92 | destroy: function () { 93 | module.verbose('Destroying previous calendar for', element); 94 | $module.removeData(moduleNamespace); 95 | module.unbind.events(); 96 | }, 97 | 98 | setup: { 99 | popup: function () { 100 | if (settings.inline) { 101 | return; 102 | } 103 | if (!$activator.length) { 104 | $activator = $module.children().first(); 105 | if (!$activator.length) { 106 | return; 107 | } 108 | } 109 | if ($.fn.popup === undefined) { 110 | module.error(error.popup); 111 | return; 112 | } 113 | if (!$container.length) { 114 | //prepend the popup element to the activator's parent so that it has less chance of messing with 115 | //the styling (eg input action button needs to be the last child to have correct border radius) 116 | $container = $('
').addClass(className.popup).prependTo($activator.parent()); 117 | } 118 | $container.addClass(className.calendar); 119 | var onVisible = settings.onVisible; 120 | var onHidden = settings.onHidden; 121 | if (!$input.length) { 122 | //no input, $container has to handle focus/blur 123 | $container.attr('tabindex', '0'); 124 | onVisible = function () { 125 | module.focus(); 126 | return settings.onVisible.apply($container, arguments); 127 | }; 128 | onHidden = function () { 129 | module.blur(); 130 | return settings.onHidden.apply($container, arguments); 131 | }; 132 | } 133 | var onShow = function () { 134 | //reset the focus date onShow 135 | module.set.focusDate(module.get.date()); 136 | module.set.mode(settings.startMode); 137 | return settings.onShow.apply($container, arguments); 138 | }; 139 | var on = settings.on || ($input.length ? 'focus' : 'click'); 140 | var options = $.extend({}, settings.popupOptions, { 141 | popup: $container, 142 | on: on, 143 | hoverable: on === 'hover', 144 | onShow: onShow, 145 | onVisible: onVisible, 146 | onHide: settings.onHide, 147 | onHidden: onHidden 148 | }); 149 | module.popup(options); 150 | }, 151 | inline: function () { 152 | if ($activator.length && !settings.inline) { 153 | return; 154 | } 155 | $container = $('
').addClass(className.calendar).appendTo($module); 156 | if (!$input.length) { 157 | $container.attr('tabindex', '0'); 158 | } 159 | }, 160 | input: function () { 161 | if (settings.touchReadonly && $input.length && isTouch) { 162 | $input.prop('readonly', true); 163 | } 164 | }, 165 | date: function () { 166 | if ($input.length) { 167 | var val = $input.val(); 168 | var date = parser.date(val, settings); 169 | module.set.date(date, settings.formatInput, false); 170 | } 171 | } 172 | }, 173 | 174 | create: { 175 | calendar: function () { 176 | var i, r, c, p, row, cell, pageGrid; 177 | 178 | var mode = module.get.mode(); 179 | var today = new Date(); 180 | var date = module.get.date(); 181 | var focusDate = module.get.focusDate(); 182 | var display = focusDate || date || settings.initialDate || today; 183 | display = module.helper.dateInRange(display); 184 | 185 | if (!focusDate) { 186 | focusDate = display; 187 | module.set.focusDate(focusDate, false, false); 188 | } 189 | 190 | var isYear = mode === 'year'; 191 | var isMonth = mode === 'month'; 192 | var isDay = mode === 'day'; 193 | var isHour = mode === 'hour'; 194 | var isMinute = mode === 'minute'; 195 | var isTimeOnly = settings.type === 'time'; 196 | 197 | var multiMonth = Math.max(settings.multiMonth, 1); 198 | var monthOffset = !isDay ? 0 : module.get.monthOffset(); 199 | 200 | var minute = display.getMinutes(); 201 | var hour = display.getHours(); 202 | var day = display.getDate(); 203 | var startMonth = display.getMonth() + monthOffset; 204 | var year = display.getFullYear(); 205 | 206 | var columns = isDay ? 7 : isHour ? 4 : 3; 207 | var columnsString = columns === 7 ? 'seven' : columns === 4 ? 'four' : 'three'; 208 | var rows = isDay || isHour ? 6 : 4; 209 | var pages = isDay ? multiMonth : 1; 210 | 211 | var container = $container; 212 | container.empty(); 213 | if (pages > 1) { 214 | pageGrid = $('
').addClass(className.grid).appendTo(container); 215 | } 216 | 217 | for (p = 0; p < pages; p++) { 218 | if (pages > 1) { 219 | var pageColumn = $('
').addClass(className.column).appendTo(pageGrid); 220 | container = pageColumn; 221 | } 222 | 223 | var month = startMonth + p; 224 | var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7; 225 | if (!settings.constantHeight && isDay) { 226 | var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn; 227 | rows = Math.ceil(requiredCells / 7); 228 | } 229 | 230 | var yearChange = isYear ? 10 : isMonth ? 1 : 0; 231 | var monthChange = isDay ? 1 : 0; 232 | var dayChange = isHour || isMinute ? 1 : 0; 233 | var prevNextDay = isHour || isMinute ? day : 1; 234 | var prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour); 235 | var nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour); 236 | 237 | var prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) : 238 | isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1); 239 | var nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) : 240 | isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1); 241 | 242 | var table = $('').addClass(className.table).addClass(columnsString + ' column').addClass(mode).appendTo(container); 243 | 244 | //no header for time-only mode 245 | if (!isTimeOnly) { 246 | var thead = $('').appendTo(table); 247 | 248 | row = $('').appendTo(thead); 249 | cell = $('').appendTo(thead); 275 | for (i = 0; i < columns; i++) { 276 | cell = $('').appendTo(table); 283 | i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0; 284 | for (r = 0; r < rows; r++) { 285 | row = $('').appendTo(tbody); 286 | for (c = 0; c < columns; c++, i++) { 287 | var cellDate = isYear ? new Date(i, month, 1, hour, minute) : 288 | isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) : 289 | isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * 5); 290 | var cellText = isYear ? i : 291 | isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() : 292 | formatter.time(cellDate, settings, true); 293 | cell = $('').appendTo(tbody); 327 | var todayButton = $('
').attr('colspan', '' + columns).appendTo(row); 250 | 251 | var headerDate = isYear || isMonth ? new Date(year, 0, 1) : 252 | isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute); 253 | var headerText = $('').addClass(className.link).appendTo(cell); 254 | headerText.text(formatter.header(headerDate, mode, settings)); 255 | var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') : 256 | isDay ? (settings.disableMonth ? 'year' : 'month') : 'day'; 257 | headerText.data(metadata.mode, newMode); 258 | 259 | if (p === 0) { 260 | var prev = $('').addClass(className.prev).appendTo(cell); 261 | prev.data(metadata.focusDate, prevDate); 262 | prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode)); 263 | $('').addClass(className.prevIcon).appendTo(prev); 264 | } 265 | 266 | if (p === pages - 1) { 267 | var next = $('').addClass(className.next).appendTo(cell); 268 | next.data(metadata.focusDate, nextDate); 269 | next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode)); 270 | $('').addClass(className.nextIcon).appendTo(next); 271 | } 272 | 273 | if (isDay) { 274 | row = $('
').appendTo(row); 277 | cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings)); 278 | } 279 | } 280 | } 281 | 282 | var tbody = $('
').addClass(className.cell).appendTo(row); 294 | cell.text(cellText); 295 | cell.data(metadata.date, cellDate); 296 | var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12); 297 | var disabled = adjacent || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode); 298 | var active = module.helper.dateEqual(cellDate, date, mode); 299 | var isToday = module.helper.dateEqual(cellDate, today, mode); 300 | cell.toggleClass(className.adjacentCell, adjacent); 301 | cell.toggleClass(className.disabledCell, disabled); 302 | cell.toggleClass(className.activeCell, active && !adjacent); 303 | if (!isHour && !isMinute) { 304 | cell.toggleClass(className.todayCell, !adjacent && isToday); 305 | } 306 | 307 | // Allow for external modifications of each cell 308 | var cellOptions = { 309 | mode: mode, 310 | adjacent: adjacent, 311 | disabled: disabled, 312 | active: active, 313 | today: isToday 314 | }; 315 | formatter.cell(cell, cellDate, cellOptions); 316 | 317 | if (module.helper.dateEqual(cellDate, focusDate, mode)) { 318 | //ensure that the focus date is exactly equal to the cell date 319 | //so that, if selected, the correct value is set 320 | module.set.focusDate(cellDate, false, false); 321 | } 322 | } 323 | } 324 | 325 | if (settings.today) { 326 | var todayRow = $('
').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow); 328 | todayButton.text(formatter.today(settings)); 329 | todayButton.data(metadata.date, today); 330 | } 331 | 332 | module.update.focus(false, table); 333 | } 334 | } 335 | }, 336 | 337 | update: { 338 | focus: function (updateRange, container) { 339 | container = container || $container; 340 | var mode = module.get.mode(); 341 | var date = module.get.date(); 342 | var focusDate = module.get.focusDate(); 343 | var startDate = module.get.startDate(); 344 | var endDate = module.get.endDate(); 345 | var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null); 346 | 347 | container.find('td').each(function () { 348 | var cell = $(this); 349 | var cellDate = cell.data(metadata.date); 350 | if (!cellDate) { 351 | return; 352 | } 353 | var disabled = cell.hasClass(className.disabledCell); 354 | var active = cell.hasClass(className.activeCell); 355 | var adjacent = cell.hasClass(className.adjacentCell); 356 | var focused = module.helper.dateEqual(cellDate, focusDate, mode); 357 | var inRange = !rangeDate ? false : 358 | ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) || 359 | (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate))); 360 | cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && !adjacent); 361 | cell.toggleClass(className.rangeCell, inRange && !active && !disabled); 362 | }); 363 | } 364 | }, 365 | 366 | refresh: function () { 367 | module.create.calendar(); 368 | }, 369 | 370 | bind: { 371 | events: function () { 372 | $container.on('mousedown' + eventNamespace, module.event.mousedown); 373 | $container.on('touchstart' + eventNamespace, module.event.mousedown); 374 | $container.on('mouseup' + eventNamespace, module.event.mouseup); 375 | $container.on('touchend' + eventNamespace, module.event.mouseup); 376 | $container.on('mouseover' + eventNamespace, module.event.mouseover); 377 | if ($input.length) { 378 | $input.on('input' + eventNamespace, module.event.inputChange); 379 | $input.on('focus' + eventNamespace, module.event.inputFocus); 380 | $input.on('blur' + eventNamespace, module.event.inputBlur); 381 | $input.on('click' + eventNamespace, module.event.inputClick); 382 | $input.on('keydown' + eventNamespace, module.event.keydown); 383 | } else { 384 | $container.on('keydown' + eventNamespace, module.event.keydown); 385 | } 386 | } 387 | }, 388 | 389 | unbind: { 390 | events: function () { 391 | $container.off(eventNamespace); 392 | if ($input.length) { 393 | $input.off(eventNamespace); 394 | } 395 | } 396 | }, 397 | 398 | event: { 399 | mouseover: function (event) { 400 | var target = $(event.target); 401 | var date = target.data(metadata.date); 402 | var mousedown = event.buttons === 1; 403 | if (date) { 404 | module.set.focusDate(date, false, true, mousedown); 405 | } 406 | }, 407 | mousedown: function (event) { 408 | if ($input.length) { 409 | //prevent the mousedown on the calendar causing the input to lose focus 410 | event.preventDefault(); 411 | } 412 | isTouchDown = event.type.indexOf('touch') >= 0; 413 | var target = $(event.target); 414 | var date = target.data(metadata.date); 415 | if (date) { 416 | module.set.focusDate(date, false, true, true); 417 | } 418 | }, 419 | mouseup: function (event) { 420 | //ensure input has focus so that it receives keydown events for calendar navigation 421 | module.focus(); 422 | event.preventDefault(); 423 | event.stopPropagation(); 424 | isTouchDown = false; 425 | var target = $(event.target); 426 | var parent = target.parent(); 427 | if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) { 428 | //clicked on a child element, switch to parent (used when clicking directly on prev/next icon element) 429 | target = parent; 430 | } 431 | var date = target.data(metadata.date); 432 | var focusDate = target.data(metadata.focusDate); 433 | var mode = target.data(metadata.mode); 434 | if (date) { 435 | var forceSet = target.hasClass(className.today); 436 | module.selectDate(date, forceSet); 437 | } 438 | else if (focusDate) { 439 | module.set.focusDate(focusDate); 440 | } 441 | else if (mode) { 442 | module.set.mode(mode); 443 | } 444 | }, 445 | keydown: function (event) { 446 | if (event.keyCode === 27 || event.keyCode === 9) { 447 | //esc || tab 448 | module.popup('hide'); 449 | } 450 | 451 | if (module.popup('is visible')) { 452 | if (event.keyCode === 37 || event.keyCode === 38 || event.keyCode === 39 || event.keyCode === 40) { 453 | //arrow keys 454 | var mode = module.get.mode(); 455 | var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : 3; 456 | var increment = event.keyCode === 37 ? -1 : event.keyCode === 38 ? -bigIncrement : event.keyCode == 39 ? 1 : bigIncrement; 457 | increment *= mode === 'minute' ? 5 : 1; 458 | var focusDate = module.get.focusDate() || module.get.date() || new Date(); 459 | var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0); 460 | var month = focusDate.getMonth() + (mode === 'month' ? increment : 0); 461 | var day = focusDate.getDate() + (mode === 'day' ? increment : 0); 462 | var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0); 463 | var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0); 464 | var newFocusDate = new Date(year, month, day, hour, minute); 465 | if (settings.type === 'time') { 466 | newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate); 467 | } 468 | if (module.helper.isDateInRange(newFocusDate, mode)) { 469 | module.set.focusDate(newFocusDate); 470 | } 471 | } else if (event.keyCode === 13) { 472 | //enter 473 | var mode = module.get.mode(); 474 | var date = module.get.focusDate(); 475 | if (date && !settings.isDisabled(date, mode)) { 476 | module.selectDate(date); 477 | } 478 | //disable form submission: 479 | event.preventDefault(); 480 | event.stopPropagation(); 481 | } 482 | } 483 | 484 | if (event.keyCode === 38 || event.keyCode === 40) { 485 | //arrow-up || arrow-down 486 | event.preventDefault(); //don't scroll 487 | module.popup('show'); 488 | } 489 | }, 490 | inputChange: function () { 491 | var val = $input.val(); 492 | var date = parser.date(val, settings); 493 | module.set.date(date, false); 494 | }, 495 | inputFocus: function () { 496 | $container.addClass(className.active); 497 | }, 498 | inputBlur: function () { 499 | $container.removeClass(className.active); 500 | if (settings.formatInput) { 501 | var date = module.get.date(); 502 | var text = formatter.datetime(date, settings); 503 | $input.val(text); 504 | } 505 | }, 506 | inputClick: function () { 507 | module.popup('show'); 508 | } 509 | }, 510 | 511 | get: { 512 | date: function () { 513 | return $module.data(metadata.date) || null; 514 | }, 515 | focusDate: function () { 516 | return $module.data(metadata.focusDate) || null; 517 | }, 518 | startDate: function () { 519 | var startModule = module.get.calendarModule(settings.startCalendar); 520 | return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null; 521 | }, 522 | endDate: function () { 523 | var endModule = module.get.calendarModule(settings.endCalendar); 524 | return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null; 525 | }, 526 | monthOffset: function () { 527 | return $module.data(metadata.monthOffset) || 0; 528 | }, 529 | mode: function () { 530 | //only returns valid modes for the current settings 531 | var mode = $module.data(metadata.mode) || settings.startMode; 532 | var validModes = module.get.validModes(); 533 | if ($.inArray(mode, validModes) >= 0) { 534 | return mode; 535 | } 536 | return settings.type === 'time' ? 'hour' : 537 | settings.type === 'month' ? 'month' : 538 | settings.type === 'year' ? 'year' : 'day'; 539 | }, 540 | validModes: function () { 541 | var validModes = []; 542 | if (settings.type !== 'time') { 543 | if (!settings.disableYear || settings.type === 'year') { 544 | validModes.push('year'); 545 | } 546 | if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') { 547 | validModes.push('month'); 548 | } 549 | if (settings.type.indexOf('date') >= 0) { 550 | validModes.push('day'); 551 | } 552 | } 553 | if (settings.type.indexOf('time') >= 0) { 554 | validModes.push('hour'); 555 | if (!settings.disableMinute) { 556 | validModes.push('minute'); 557 | } 558 | } 559 | return validModes; 560 | }, 561 | isTouch: function () { 562 | try { 563 | document.createEvent('TouchEvent'); 564 | return true; 565 | } 566 | catch (e) { 567 | return false; 568 | } 569 | }, 570 | calendarModule: function (selector) { 571 | if (!selector) { 572 | return null; 573 | } 574 | if (!(selector instanceof $)) { 575 | selector = $module.parent().children(selector).first(); 576 | } 577 | //assume range related calendars are using the same namespace 578 | return selector.data(moduleNamespace); 579 | } 580 | }, 581 | 582 | set: { 583 | date: function (date, updateInput, fireChange) { 584 | updateInput = updateInput !== false; 585 | fireChange = fireChange !== false; 586 | date = module.helper.sanitiseDate(date); 587 | date = module.helper.dateInRange(date); 588 | 589 | var mode = module.get.mode(); 590 | var text = formatter.datetime(date, settings); 591 | if (fireChange && settings.onChange.call(element, date, text, mode) === false) { 592 | return false; 593 | } 594 | 595 | module.set.focusDate(date); 596 | 597 | if (settings.isDisabled(date, mode)) { 598 | return false; 599 | } 600 | 601 | var endDate = module.get.endDate(); 602 | if (!!endDate && !!date && date > endDate) { 603 | //selected date is greater than end date in range, so clear end date 604 | module.set.endDate(undefined); 605 | } 606 | module.set.dataKeyValue(metadata.date, date); 607 | 608 | if (updateInput && $input.length) { 609 | $input.val(text); 610 | } 611 | }, 612 | startDate: function (date, refreshCalendar) { 613 | date = module.helper.sanitiseDate(date); 614 | var startModule = module.get.calendarModule(settings.startCalendar); 615 | if (startModule) { 616 | startModule.set.date(date); 617 | } 618 | module.set.dataKeyValue(metadata.startDate, date, refreshCalendar); 619 | }, 620 | endDate: function (date, refreshCalendar) { 621 | date = module.helper.sanitiseDate(date); 622 | var endModule = module.get.calendarModule(settings.endCalendar); 623 | if (endModule) { 624 | endModule.set.date(date); 625 | } 626 | module.set.dataKeyValue(metadata.endDate, date, refreshCalendar); 627 | }, 628 | focusDate: function (date, refreshCalendar, updateFocus, updateRange) { 629 | date = module.helper.sanitiseDate(date); 630 | date = module.helper.dateInRange(date); 631 | var isDay = module.get.mode() === 'day'; 632 | var oldFocusDate = module.get.focusDate(); 633 | if (isDay && date && oldFocusDate) { 634 | var yearDelta = date.getFullYear() - oldFocusDate.getFullYear(); 635 | var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth(); 636 | if (monthDelta) { 637 | var monthOffset = module.get.monthOffset() - monthDelta; 638 | module.set.monthOffset(monthOffset, false); 639 | } 640 | } 641 | var changed = module.set.dataKeyValue(metadata.focusDate, date, refreshCalendar); 642 | updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange; 643 | focusDateUsedForRange = updateRange; 644 | if (updateFocus) { 645 | module.update.focus(updateRange); 646 | } 647 | }, 648 | monthOffset: function (monthOffset, refreshCalendar) { 649 | var multiMonth = Math.max(settings.multiMonth, 1); 650 | monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset)); 651 | module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar); 652 | }, 653 | mode: function (mode, refreshCalendar) { 654 | module.set.dataKeyValue(metadata.mode, mode, refreshCalendar); 655 | }, 656 | dataKeyValue: function (key, value, refreshCalendar) { 657 | var oldValue = $module.data(key); 658 | var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects 659 | if (value) { 660 | $module.data(key, value); 661 | } else { 662 | $module.removeData(key); 663 | } 664 | refreshCalendar = refreshCalendar !== false && !equal; 665 | if (refreshCalendar) { 666 | module.create.calendar(); 667 | } 668 | return !equal; 669 | } 670 | }, 671 | 672 | selectDate: function (date, forceSet) { 673 | var mode = module.get.mode(); 674 | var complete = forceSet || mode === 'minute' || 675 | (settings.disableMinute && mode === 'hour') || 676 | (settings.type === 'date' && mode === 'day') || 677 | (settings.type === 'month' && mode === 'month') || 678 | (settings.type === 'year' && mode === 'year'); 679 | if (complete) { 680 | var canceled = module.set.date(date) === false; 681 | if (!canceled && settings.closable) { 682 | module.popup('hide'); 683 | //if this is a range calendar, show the end date calendar popup and focus the input 684 | var endModule = module.get.calendarModule(settings.endCalendar); 685 | if (endModule) { 686 | endModule.popup('show'); 687 | endModule.focus(); 688 | } 689 | } 690 | } else { 691 | var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') : 692 | mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute'; 693 | module.set.mode(newMode); 694 | if (mode === 'hour' || (mode === 'day' && module.get.date())) { 695 | //the user has chosen enough to consider a valid date/time has been chosen 696 | module.set.date(date); 697 | } else { 698 | module.set.focusDate(date); 699 | } 700 | } 701 | }, 702 | 703 | changeDate: function (date) { 704 | module.set.date(date); 705 | }, 706 | 707 | clear: function () { 708 | module.set.date(undefined); 709 | }, 710 | 711 | popup: function () { 712 | return $activator.popup.apply($activator, arguments); 713 | }, 714 | 715 | focus: function () { 716 | if ($input.length) { 717 | $input.focus(); 718 | } else { 719 | $container.focus(); 720 | } 721 | }, 722 | blur: function () { 723 | if ($input.length) { 724 | $input.blur(); 725 | } else { 726 | $container.blur(); 727 | } 728 | }, 729 | 730 | helper: { 731 | sanitiseDate: function (date) { 732 | if (!date) { 733 | return undefined; 734 | } 735 | if (!(date instanceof Date)) { 736 | date = parser.date('' + date, settings); 737 | } 738 | if (isNaN(date.getTime())) { 739 | return undefined; 740 | } 741 | return date; 742 | }, 743 | dateDiff: function (date1, date2, mode) { 744 | mode = mode || 'day'; 745 | var isTimeOnly = settings.type === 'time'; 746 | var isYear = mode === 'year'; 747 | var isYearOrMonth = isYear || mode === 'month'; 748 | var isMinute = mode === 'minute'; 749 | var isHourOrMinute = isMinute || mode === 'hour'; 750 | //only care about a minute accuracy of 5 751 | date1 = new Date( 752 | isTimeOnly ? 2000 : date1.getFullYear(), 753 | isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(), 754 | isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(), 755 | !isHourOrMinute ? 0 : date1.getHours(), 756 | !isMinute ? 0 : 5 * Math.floor(date1.getMinutes() / 5)); 757 | date2 = new Date( 758 | isTimeOnly ? 2000 : date2.getFullYear(), 759 | isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(), 760 | isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(), 761 | !isHourOrMinute ? 0 : date2.getHours(), 762 | !isMinute ? 0 : 5 * Math.floor(date2.getMinutes() / 5)); 763 | return date2.getTime() - date1.getTime(); 764 | }, 765 | dateEqual: function (date1, date2, mode) { 766 | return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0; 767 | }, 768 | isDateInRange: function (date, mode, minDate, maxDate) { 769 | if (!minDate && !maxDate) { 770 | var startDate = module.get.startDate(); 771 | minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; 772 | maxDate = settings.maxDate; 773 | } 774 | minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), 5 * Math.ceil(minDate.getMinutes() / 5)); 775 | return !(!date || 776 | (minDate && module.helper.dateDiff(date, minDate, mode) > 0) || 777 | (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0)); 778 | }, 779 | dateInRange: function (date, minDate, maxDate) { 780 | if (!minDate && !maxDate) { 781 | var startDate = module.get.startDate(); 782 | minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; 783 | maxDate = settings.maxDate; 784 | } 785 | minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), 5 * Math.ceil(minDate.getMinutes() / 5)); 786 | var isTimeOnly = settings.type === 'time'; 787 | return !date ? date : 788 | (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ? 789 | (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) : 790 | (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ? 791 | (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) : 792 | date; 793 | }, 794 | mergeDateTime: function (date, time) { 795 | return (!date || !time) ? time : 796 | new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes()); 797 | } 798 | }, 799 | 800 | setting: function (name, value) { 801 | module.debug('Changing setting', name, value); 802 | if ($.isPlainObject(name)) { 803 | $.extend(true, settings, name); 804 | } 805 | else if (value !== undefined) { 806 | if ($.isPlainObject(settings[name])) { 807 | $.extend(true, settings[name], value); 808 | } 809 | else { 810 | settings[name] = value; 811 | } 812 | } 813 | else { 814 | return settings[name]; 815 | } 816 | }, 817 | internal: function (name, value) { 818 | module.debug('Changing internal', name, value); 819 | if (value !== undefined) { 820 | if ($.isPlainObject(name)) { 821 | $.extend(true, module, name); 822 | } 823 | else { 824 | module[name] = value; 825 | } 826 | } 827 | else { 828 | return module[name]; 829 | } 830 | }, 831 | debug: function () { 832 | if (!settings.silent && settings.debug) { 833 | if (settings.performance) { 834 | module.performance.log(arguments); 835 | } 836 | else { 837 | module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); 838 | module.debug.apply(console, arguments); 839 | } 840 | } 841 | }, 842 | verbose: function () { 843 | if (!settings.silent && settings.verbose && settings.debug) { 844 | if (settings.performance) { 845 | module.performance.log(arguments); 846 | } 847 | else { 848 | module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); 849 | module.verbose.apply(console, arguments); 850 | } 851 | } 852 | }, 853 | error: function () { 854 | if (!settings.silent) { 855 | module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); 856 | module.error.apply(console, arguments); 857 | } 858 | }, 859 | performance: { 860 | log: function (message) { 861 | var 862 | currentTime, 863 | executionTime, 864 | previousTime 865 | ; 866 | if (settings.performance) { 867 | currentTime = new Date().getTime(); 868 | previousTime = time || currentTime; 869 | executionTime = currentTime - previousTime; 870 | time = currentTime; 871 | performance.push({ 872 | 'Name': message[0], 873 | 'Arguments': [].slice.call(message, 1) || '', 874 | 'Element': element, 875 | 'Execution Time': executionTime 876 | }); 877 | } 878 | clearTimeout(module.performance.timer); 879 | module.performance.timer = setTimeout(module.performance.display, 500); 880 | }, 881 | display: function () { 882 | var 883 | title = settings.name + ':', 884 | totalTime = 0 885 | ; 886 | time = false; 887 | clearTimeout(module.performance.timer); 888 | $.each(performance, function (index, data) { 889 | totalTime += data['Execution Time']; 890 | }); 891 | title += ' ' + totalTime + 'ms'; 892 | if (moduleSelector) { 893 | title += ' \'' + moduleSelector + '\''; 894 | } 895 | if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) { 896 | console.groupCollapsed(title); 897 | if (console.table) { 898 | console.table(performance); 899 | } 900 | else { 901 | $.each(performance, function (index, data) { 902 | console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms'); 903 | }); 904 | } 905 | console.groupEnd(); 906 | } 907 | performance = []; 908 | } 909 | }, 910 | invoke: function (query, passedArguments, context) { 911 | var 912 | object = instance, 913 | maxDepth, 914 | found, 915 | response 916 | ; 917 | passedArguments = passedArguments || queryArguments; 918 | context = element || context; 919 | if (typeof query == 'string' && object !== undefined) { 920 | query = query.split(/[\. ]/); 921 | maxDepth = query.length - 1; 922 | $.each(query, function (depth, value) { 923 | var camelCaseValue = (depth != maxDepth) 924 | ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) 925 | : query 926 | ; 927 | if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) { 928 | object = object[camelCaseValue]; 929 | } 930 | else if (object[camelCaseValue] !== undefined) { 931 | found = object[camelCaseValue]; 932 | return false; 933 | } 934 | else if ($.isPlainObject(object[value]) && (depth != maxDepth)) { 935 | object = object[value]; 936 | } 937 | else if (object[value] !== undefined) { 938 | found = object[value]; 939 | return false; 940 | } 941 | else { 942 | module.error(error.method, query); 943 | return false; 944 | } 945 | }); 946 | } 947 | if ($.isFunction(found)) { 948 | response = found.apply(context, passedArguments); 949 | } 950 | else if (found !== undefined) { 951 | response = found; 952 | } 953 | if ($.isArray(returnedValue)) { 954 | returnedValue.push(response); 955 | } 956 | else if (returnedValue !== undefined) { 957 | returnedValue = [returnedValue, response]; 958 | } 959 | else if (response !== undefined) { 960 | returnedValue = response; 961 | } 962 | return found; 963 | } 964 | }; 965 | 966 | if (methodInvoked) { 967 | if (instance === undefined) { 968 | module.initialize(); 969 | } 970 | module.invoke(query); 971 | } 972 | else { 973 | if (instance !== undefined) { 974 | instance.invoke('destroy'); 975 | } 976 | module.initialize(); 977 | } 978 | }) 979 | ; 980 | return (returnedValue !== undefined) 981 | ? returnedValue 982 | : this 983 | ; 984 | }; 985 | 986 | $.fn.calendar.settings = { 987 | 988 | name: 'Calendar', 989 | namespace: 'calendar', 990 | 991 | silent: false, 992 | debug: false, 993 | verbose: false, 994 | performance: false, 995 | 996 | type: 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year' 997 | firstDayOfWeek: 0, // day for first day column (0 = Sunday) 998 | constantHeight: true, // add rows to shorter months to keep day calendar height consistent (6 rows) 999 | today: false, // show a 'today/now' button at the bottom of the calendar 1000 | closable: true, // close the popup after selecting a date/time 1001 | monthFirst: true, // month before day when parsing/converting date from/to text 1002 | touchReadonly: true, // set input to readonly on touch devices 1003 | inline: false, // create the calendar inline instead of inside a popup 1004 | on: null, // when to show the popup (defaults to 'focus' for input, 'click' for others) 1005 | initialDate: null, // date to display initially when no date is selected (null = now) 1006 | startMode: false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day') 1007 | minDate: null, // minimum date/time that can be selected, dates/times before are disabled 1008 | maxDate: null, // maximum date/time that can be selected, dates/times after are disabled 1009 | ampm: true, // show am/pm in time mode 1010 | disableYear: false, // disable year selection mode 1011 | disableMonth: false, // disable month selection mode 1012 | disableMinute: false, // disable minute selection mode 1013 | formatInput: true, // format the input text upon input blur and module creation 1014 | startCalendar: null, // jquery object or selector for another calendar that represents the start date of a date range 1015 | endCalendar: null, // jquery object or selector for another calendar that represents the end date of a date range 1016 | multiMonth: 1, // show multiple months when in 'day' mode 1017 | 1018 | // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden) 1019 | popupOptions: { 1020 | position: 'bottom left', 1021 | lastResort: 'bottom left', 1022 | prefer: 'opposite', 1023 | hideOnScroll: false 1024 | }, 1025 | 1026 | text: { 1027 | days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 1028 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 1029 | monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 1030 | today: 'Today', 1031 | now: 'Now', 1032 | am: 'AM', 1033 | pm: 'PM' 1034 | }, 1035 | 1036 | formatter: { 1037 | header: function (date, mode, settings) { 1038 | return mode === 'year' ? settings.formatter.yearHeader(date, settings) : 1039 | mode === 'month' ? settings.formatter.monthHeader(date, settings) : 1040 | mode === 'day' ? settings.formatter.dayHeader(date, settings) : 1041 | mode === 'hour' ? settings.formatter.hourHeader(date, settings) : 1042 | settings.formatter.minuteHeader(date, settings); 1043 | }, 1044 | yearHeader: function (date, settings) { 1045 | var decadeYear = Math.ceil(date.getFullYear() / 10) * 10; 1046 | return (decadeYear - 9) + ' - ' + (decadeYear + 2); 1047 | }, 1048 | monthHeader: function (date, settings) { 1049 | return date.getFullYear(); 1050 | }, 1051 | dayHeader: function (date, settings) { 1052 | var month = settings.text.months[date.getMonth()]; 1053 | var year = date.getFullYear(); 1054 | return month + ' ' + year; 1055 | }, 1056 | hourHeader: function (date, settings) { 1057 | return settings.formatter.date(date, settings); 1058 | }, 1059 | minuteHeader: function (date, settings) { 1060 | return settings.formatter.date(date, settings); 1061 | }, 1062 | dayColumnHeader: function (day, settings) { 1063 | return settings.text.days[day]; 1064 | }, 1065 | datetime: function (date, settings) { 1066 | if (!date) { 1067 | return ''; 1068 | } 1069 | var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings); 1070 | var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false); 1071 | var separator = settings.type === 'datetime' ? ' ' : ''; 1072 | return day + separator + time; 1073 | }, 1074 | date: function (date, settings) { 1075 | if (!date) { 1076 | return ''; 1077 | } 1078 | var day = date.getDate(); 1079 | var month = settings.text.months[date.getMonth()]; 1080 | var year = date.getFullYear(); 1081 | return settings.type === 'year' ? year : 1082 | settings.type === 'month' ? month + ' ' + year : 1083 | (settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year; 1084 | }, 1085 | time: function (date, settings, forCalendar) { 1086 | if (!date) { 1087 | return ''; 1088 | } 1089 | var hour = date.getHours(); 1090 | var minute = date.getMinutes(); 1091 | var ampm = ''; 1092 | if (settings.ampm) { 1093 | ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm); 1094 | hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; 1095 | } 1096 | return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm; 1097 | }, 1098 | today: function (settings) { 1099 | return settings.type === 'date' ? settings.text.today : settings.text.now; 1100 | }, 1101 | cell: function (cell, date, cellOptions) { 1102 | } 1103 | }, 1104 | 1105 | parser: { 1106 | date: function (text, settings) { 1107 | if (!text) { 1108 | return null; 1109 | } 1110 | text = ('' + text).trim().toLowerCase(); 1111 | if (text.length === 0) { 1112 | return null; 1113 | } 1114 | 1115 | var i, j, k; 1116 | var minute = -1, hour = -1, day = -1, month = -1, year = -1; 1117 | var isAm = undefined; 1118 | 1119 | var isTimeOnly = settings.type === 'time'; 1120 | var isDateOnly = settings.type.indexOf('time') < 0; 1121 | 1122 | var words = text.split(settings.regExp.dateWords); 1123 | var numbers = text.split(settings.regExp.dateNumbers); 1124 | 1125 | if (!isDateOnly) { 1126 | //am/pm 1127 | isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true : 1128 | $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined; 1129 | 1130 | //time with ':' 1131 | for (i = 0; i < numbers.length; i++) { 1132 | var number = numbers[i]; 1133 | if (number.indexOf(':') >= 0) { 1134 | if (hour < 0 || minute < 0) { 1135 | var parts = number.split(':'); 1136 | for (k = 0; k < Math.min(2, parts.length); k++) { 1137 | j = parseInt(parts[k]); 1138 | if (isNaN(j)) { 1139 | j = 0; 1140 | } 1141 | if (k === 0) { 1142 | hour = j % 24; 1143 | } else { 1144 | minute = j % 60; 1145 | } 1146 | } 1147 | } 1148 | numbers.splice(i, 1); 1149 | } 1150 | } 1151 | } 1152 | 1153 | if (!isTimeOnly) { 1154 | //textual month 1155 | for (i = 0; i < words.length; i++) { 1156 | var word = words[i]; 1157 | if (word.length <= 0) { 1158 | continue; 1159 | } 1160 | word = word.substring(0, Math.min(word.length, 3)); 1161 | for (j = 0; j < settings.text.months.length; j++) { 1162 | var monthString = settings.text.months[j]; 1163 | monthString = monthString.substring(0, Math.min(word.length, Math.min(monthString.length, 3))).toLowerCase(); 1164 | if (monthString === word) { 1165 | month = j + 1; 1166 | break; 1167 | } 1168 | } 1169 | if (month >= 0) { 1170 | break; 1171 | } 1172 | } 1173 | 1174 | //year > 59 1175 | for (i = 0; i < numbers.length; i++) { 1176 | j = parseInt(numbers[i]); 1177 | if (isNaN(j)) { 1178 | continue; 1179 | } 1180 | if (j > 59) { 1181 | year = j; 1182 | numbers.splice(i, 1); 1183 | break; 1184 | } 1185 | } 1186 | 1187 | //numeric month 1188 | if (month < 0) { 1189 | for (i = 0; i < numbers.length; i++) { 1190 | k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1; 1191 | j = parseInt(numbers[k]); 1192 | if (isNaN(j)) { 1193 | continue; 1194 | } 1195 | if (1 <= j && j <= 12) { 1196 | month = j; 1197 | numbers.splice(k, 1); 1198 | break; 1199 | } 1200 | } 1201 | } 1202 | 1203 | //day 1204 | for (i = 0; i < numbers.length; i++) { 1205 | j = parseInt(numbers[i]); 1206 | if (isNaN(j)) { 1207 | continue; 1208 | } 1209 | if (1 <= j && j <= 31) { 1210 | day = j; 1211 | numbers.splice(i, 1); 1212 | break; 1213 | } 1214 | } 1215 | 1216 | //year <= 59 1217 | if (year < 0) { 1218 | for (i = numbers.length - 1; i >= 0; i--) { 1219 | j = parseInt(numbers[i]); 1220 | if (isNaN(j)) { 1221 | continue; 1222 | } 1223 | if (j < 99) { 1224 | j += 2000; 1225 | } 1226 | year = j; 1227 | numbers.splice(i, 1); 1228 | break; 1229 | } 1230 | } 1231 | } 1232 | 1233 | if (!isDateOnly) { 1234 | //hour 1235 | if (hour < 0) { 1236 | for (i = 0; i < numbers.length; i++) { 1237 | j = parseInt(numbers[i]); 1238 | if (isNaN(j)) { 1239 | continue; 1240 | } 1241 | if (0 <= j && j <= 23) { 1242 | hour = j; 1243 | numbers.splice(i, 1); 1244 | break; 1245 | } 1246 | } 1247 | } 1248 | 1249 | //minute 1250 | if (minute < 0) { 1251 | for (i = 0; i < numbers.length; i++) { 1252 | j = parseInt(numbers[i]); 1253 | if (isNaN(j)) { 1254 | continue; 1255 | } 1256 | if (0 <= j && j <= 59) { 1257 | minute = j; 1258 | numbers.splice(i, 1); 1259 | break; 1260 | } 1261 | } 1262 | } 1263 | } 1264 | 1265 | if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) { 1266 | return null; 1267 | } 1268 | 1269 | if (minute < 0) { 1270 | minute = 0; 1271 | } 1272 | if (hour < 0) { 1273 | hour = 0; 1274 | } 1275 | if (day < 0) { 1276 | day = 1; 1277 | } 1278 | if (month < 0) { 1279 | month = 1; 1280 | } 1281 | if (year < 0) { 1282 | year = new Date().getFullYear(); 1283 | } 1284 | 1285 | if (isAm !== undefined) { 1286 | if (isAm) { 1287 | if (hour === 12) { 1288 | hour = 0; 1289 | } 1290 | } else if (hour < 12) { 1291 | hour += 12; 1292 | } 1293 | } 1294 | 1295 | var date = new Date(year, month - 1, day, hour, minute); 1296 | if (date.getMonth() !== month - 1 || date.getFullYear() !== year) { 1297 | //month or year don't match up, switch to last day of the month 1298 | date = new Date(year, month, 0, hour, minute); 1299 | } 1300 | return isNaN(date.getTime()) ? null : date; 1301 | } 1302 | }, 1303 | 1304 | // callback when date changes, return false to cancel the change 1305 | onChange: function (date, text, mode) { 1306 | return true; 1307 | }, 1308 | 1309 | // callback before show animation, return false to prevent show 1310 | onShow: function () { 1311 | }, 1312 | 1313 | // callback after show animation 1314 | onVisible: function () { 1315 | }, 1316 | 1317 | // callback before hide animation, return false to prevent hide 1318 | onHide: function () { 1319 | }, 1320 | 1321 | // callback after hide animation 1322 | onHidden: function () { 1323 | }, 1324 | 1325 | // is the given date disabled? 1326 | isDisabled: function (date, mode) { 1327 | return false; 1328 | }, 1329 | 1330 | selector: { 1331 | popup: '.ui.popup', 1332 | input: 'input', 1333 | activator: 'input' 1334 | }, 1335 | 1336 | regExp: { 1337 | dateWords: /[^A-Za-z\u00C0-\u024F]+/g, 1338 | dateNumbers: /[^\d:]+/g 1339 | }, 1340 | 1341 | error: { 1342 | popup: 'UI Popup, a required component is not included in this page', 1343 | method: 'The method you called is not defined.' 1344 | }, 1345 | 1346 | className: { 1347 | calendar: 'calendar', 1348 | active: 'active', 1349 | popup: 'ui popup', 1350 | grid: 'ui equal width grid', 1351 | column: 'column', 1352 | table: 'ui celled center aligned unstackable table', 1353 | prev: 'prev link', 1354 | next: 'next link', 1355 | prevIcon: 'chevron left icon', 1356 | nextIcon: 'chevron right icon', 1357 | link: 'link', 1358 | cell: 'link', 1359 | disabledCell: 'disabled', 1360 | adjacentCell: 'adjacent', 1361 | activeCell: 'active', 1362 | rangeCell: 'range', 1363 | focusCell: 'focus', 1364 | todayCell: 'today', 1365 | today: 'today link' 1366 | }, 1367 | 1368 | metadata: { 1369 | date: 'date', 1370 | focusDate: 'focusDate', 1371 | startDate: 'startDate', 1372 | endDate: 'endDate', 1373 | mode: 'mode', 1374 | monthOffset: 'monthOffset' 1375 | } 1376 | }; 1377 | 1378 | })(jQuery, window, document); 1379 | --------------------------------------------------------------------------------