├── .gitignore ├── dist ├── pikaday-responsive.min.css ├── pikaday-responsive.css ├── dependencies │ ├── pikaday.min.css │ ├── pikaday-responsive-modernizr.js │ ├── pikaday.css │ ├── pikaday.min.js │ ├── pikaday.js │ └── moment.min.js ├── pikaday-package.min.css ├── pikaday-responsive.min.js ├── pikaday-package.css └── pikaday-responsive.js ├── package.json ├── src ├── css │ ├── scss │ │ ├── pikaday-responsive.scss │ │ └── _pikaday-responsive-partial.scss │ └── pikaday-responsive.css └── pikaday-responsive.js ├── bower.json ├── upgrading-to-v0-6.md ├── LICENSE ├── demo-style.css ├── CHANGELOG.md ├── gruntfile.js ├── index.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | bower_components 4 | .sass-cache -------------------------------------------------------------------------------- /dist/pikaday-responsive.min.css: -------------------------------------------------------------------------------- 1 | .pikaday__container{display:inline-block;position:relative}.pikaday__display,.pikaday__invisible{width:100%}.pikaday__display--native{pointer-events:none;cursor:pointer}.pikaday__display.is-invalid{background:rgba(255,0,0,.05)}.pikaday__invisible{opacity:0;color:transparent;background:0 0;border:none;box-shadow:none;position:absolute;display:block;left:0;top:0;height:100%;width:100%} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pikaday-responsive", 3 | "version": "0.7.0", 4 | "devDependencies": { 5 | "grunt": "~0.4.5", 6 | "grunt-bower-install-simple": "^1.2.1", 7 | "grunt-bower-update": "^0.1.2", 8 | "grunt-contrib-concat": "^0.5.0", 9 | "grunt-contrib-cssmin": "^0.14.0", 10 | "grunt-contrib-sass": "^0.9.2", 11 | "grunt-contrib-uglify": "^0.11.1", 12 | "grunt-modernizr": "~1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/css/scss/pikaday-responsive.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * PikadayResponsive 3 | * A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. 4 | * 5 | * @author: Francesco Novy 6 | * @licence: MIT 7 | * @link https://github.com/mydea/PikadayResponsive 8 | * @copyright: (c) 2016 9 | * @version: 0.6.7 10 | */ 11 | 12 | @import "pikaday-responsive-partial"; -------------------------------------------------------------------------------- /src/css/scss/_pikaday-responsive-partial.scss: -------------------------------------------------------------------------------- 1 | .pikaday__container { 2 | display: inline-block; 3 | position: relative; 4 | } 5 | 6 | /* Height and width has to be equal! */ 7 | .pikaday__display, .pikaday__invisible { 8 | width: 100%; 9 | } 10 | 11 | .pikaday__display--native { 12 | pointer-events: none; 13 | cursor: pointer; 14 | } 15 | 16 | .pikaday__display.is-invalid { 17 | background: rgba(255, 0, 0, 0.05); 18 | } 19 | 20 | .pikaday__invisible { 21 | opacity: 0; 22 | color: transparent; 23 | background: transparent; 24 | border: none; 25 | box-shadow: none; 26 | position: absolute; 27 | display: block; 28 | left: 0; 29 | top: 0; 30 | height: 100%; 31 | width: 100%; 32 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pikaday-responsive", 3 | "version": "0.7.0", 4 | "homepage": "https://github.com/mydea/PikadayResponsive", 5 | "authors": [ 6 | "Francesco Novy " 7 | ], 8 | "description": "A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. ", 9 | "main": "dist/pikaday-responsive.js", 10 | "keywords": [ 11 | "Pikaday", 12 | "responsive", 13 | "datepicker", 14 | "date" 15 | ], 16 | "license": "MIT", 17 | "ignore": [ 18 | "**/.*", 19 | "node_modules", 20 | "bower_components", 21 | "test", 22 | "tests", 23 | "src" 24 | ], 25 | "dependencies": { 26 | "jquery": "2.2.3", 27 | "momentjs": "~2.22.2", 28 | "pikaday": "~1.6.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/css/pikaday-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * PikadayResponsive 3 | * 4 | * author: Francesco Novy 5 | * licence: MIT license 6 | * https://github.com/mydea/PikadayResponsive 7 | */ 8 | 9 | 10 | .pikaday__container { 11 | display: inline-block; 12 | position: relative; 13 | } 14 | 15 | /* Height and width has to be equal! */ 16 | .pikaday__display, .pikaday__invisible { 17 | width: 100%; 18 | } 19 | 20 | .pikaday__display--native { 21 | pointer-events: none; 22 | cursor: pointer; 23 | } 24 | 25 | .pikaday__display.is-invalid { 26 | background: rgba(255, 0, 0, 0.05); 27 | } 28 | 29 | .pikaday__invisible { 30 | opacity: 0; 31 | color: transparent; 32 | background: transparent; 33 | border: none; 34 | box-shadow: none; 35 | position: absolute; 36 | display: block; 37 | left: 0; 38 | top: 0; 39 | height: 100%; 40 | width: 100%; 41 | } 42 | 43 | /*# sourceMappingURL=pikaday-responsive.css.map */ 44 | -------------------------------------------------------------------------------- /upgrading-to-v0-6.md: -------------------------------------------------------------------------------- 1 | # Updating to 0.6.0 2 | PikadayResponsive was completely re-written for v0.6.0 and the API changed quite a bit for this version. If you want to update from < 0.6.0 to >= 0.6.0, follow these steps: 3 | 4 | * Instead of `$("#my-element").pikaday()`, you have to instantiate it with `var dateInstance = pikadayResponsive(document.getElementById("my-element"), options);` 5 | * The available options changed a bit. See the *Configuration* section for details about the available options. 6 | * The *clear* and *today* buttons have been removed. If you need this functionality, it is easy to implement it yourself with the new `dateInstance.setDate()` function. 7 | * The `displayFormat` option has been renamed to `format`. Both `format` and `outputFormat` now only take Moment.js-formats as parameters. If you want to use an UNIX-timestamp, you cans imply use `X`. 8 | 9 | It is not possible anymore to instantiate pikadayResponsive for a collection. You have to do this in a loop: 10 | 11 | ```js 12 | $elements = $(".input"); 13 | $elements.each(function() { 14 | pikadayResponsive($(this)); 15 | }); 16 | ``` 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 mydea 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/pikaday-responsive.css: -------------------------------------------------------------------------------- 1 | /* 2 | * PikadayResponsive 3 | * A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. 4 | * 5 | * @author: Francesco Novy 6 | * @licence: MIT 7 | * @link https://github.com/mydea/PikadayResponsive 8 | * @copyright: (c) 2016 9 | * @version: 0.6.7 10 | */ 11 | .pikaday__container { 12 | display: inline-block; 13 | position: relative; 14 | } 15 | 16 | /* Height and width has to be equal! */ 17 | .pikaday__display, .pikaday__invisible { 18 | width: 100%; 19 | } 20 | 21 | .pikaday__display--native { 22 | pointer-events: none; 23 | cursor: pointer; 24 | } 25 | 26 | .pikaday__display.is-invalid { 27 | background: rgba(255, 0, 0, 0.05); 28 | } 29 | 30 | .pikaday__invisible { 31 | opacity: 0; 32 | color: transparent; 33 | background: transparent; 34 | border: none; 35 | box-shadow: none; 36 | position: absolute; 37 | display: block; 38 | left: 0; 39 | top: 0; 40 | height: 100%; 41 | width: 100%; 42 | } 43 | 44 | /*# sourceMappingURL=dist/pikaday-responsive.css.map */ 45 | -------------------------------------------------------------------------------- /demo-style.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-box-sizing: border-box; 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | font-family: Roboto, 'Open Sans', 'Segoe UI', 'Helvetica Neue', 'HelveticaNeue', Helvetica, Arial, sans-serif; 8 | margin: 0; 9 | padding: 0; 10 | background: #eee; 11 | font-size: 18px; 12 | line-height: 1.3; 13 | } 14 | 15 | .wrapper { 16 | position: relative; 17 | max-width: 900px; 18 | margin: 1em auto; 19 | background: #fff; 20 | padding: 1em; 21 | } 22 | 23 | h1 { 24 | font-size: 1.5em; 25 | margin-top: 1em; 26 | } 27 | 28 | h2 { 29 | font-size: 1em; 30 | margin-bottom: 0; 31 | } 32 | 33 | p { 34 | margin: 1em 0; 35 | } 36 | 37 | a { 38 | color: #33aaff; 39 | text-decoration: none; 40 | border-bottom: 1px solid; 41 | } 42 | 43 | a:hover, a:focus { 44 | color: #528CE0; 45 | } 46 | 47 | input { 48 | font-family: Roboto, 'Open Sans', 'Segoe UI', 'Helvetica Neue', 'HelveticaNeue', Helvetica, Arial, sans-serif; 49 | font-size: 18px; 50 | padding: 3px 10px; 51 | vertical-align: middle; 52 | } 53 | 54 | label { 55 | vertical-align: middle; 56 | } 57 | 58 | .pikaday-container { 59 | display: block; 60 | width: 320px; 61 | max-width: 100%; 62 | } 63 | 64 | .output { 65 | padding: 5px; 66 | background: #33aaff; 67 | color: #fff; 68 | line-height: 1.5em; 69 | min-height: 1.5em; 70 | width: 320px; 71 | max-width: 100%; 72 | margin: 0; 73 | } 74 | .output:before { 75 | content: "> "; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | v0.7.0 4 | * Automatically add IDs to generated inputs to enable usage of labels 5 | 6 | v0.6.9 7 | * Fix bug with incorrect detection of touch events caused by update to Modernizr 3.x 8 | 9 | v0.6.8 10 | * Improve structure of repo (thanks to @olets) 11 | * Reformat whitespace 12 | 13 | v0.6.7 14 | * Fix bug which set wrong date format on pikaday fields 15 | 16 | v0.6.6 17 | 18 | * Fix a bug with dayOffset which appeared when setting a date manually via keyboard, where the dayOffset was added twice. 19 | 20 | v0.6.5 21 | 22 | * Add dayOffset option to work with timezones 23 | 24 | v0.6.4 25 | 26 | * Update Pikaday dependency to 1.4.0 27 | 28 | v0.6.3 29 | 30 | * Add optional second parameter `format` to `setDate()` 31 | 32 | v0.6.2 33 | 34 | * Initial values of inputs should be set when initialising 35 | 36 | v0.6.1 37 | 38 | * Change jQuery dependency to use v1.11.3 or greater 39 | 40 | v0.6.0 41 | 42 | * Complete re-write of the library 43 | * Remove integrated today/clear buttons 44 | * Make the function return a dateObject to work with 45 | * Add `change-date` event to input-field which receives the dateObject 46 | 47 | v0.5.3 (August 6th 2015) 48 | 49 | * Update dependencies 50 | * Check for windows as operating system to show Pikaday on touch-enabled laptops 51 | 52 | v0.5.0 (February 5th 2015) 53 | 54 | * Moved dependencies to bower 55 | * Released pikaday-responsive on bower 56 | * Improved demo 57 | * Fixed bug with changing value after initialisation 58 | * Added seperate .scss and .css files with updated pikaday-responsive styles 59 | * Some other minor fixes / restructuring -------------------------------------------------------------------------------- /dist/dependencies/pikaday.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";/*! 2 | * Pikaday 3 | * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/ 4 | */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==)}.is-rtl .pika-prev,.pika-next{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=)}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.is-disabled .pika-button,.is-outside-current-month .pika-button{opacity:.3;color:#999}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.is-disabled .pika-button,.is-selection-disabled{pointer-events:none;cursor:default}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.has-event .pika-button,.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.has-event .pika-button{background:#005da9;box-shadow:inset 0 1px 3px #0076c9}.is-disabled .pika-button,.is-inrange .pika-button{background:#D5E9F7}.is-startrange .pika-button{color:#fff;background:#6CB31D;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.pika-button:hover,.pika-row.pick-whole-week:hover .pika-button{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help} -------------------------------------------------------------------------------- /dist/dependencies/pikaday-responsive-modernizr.js: -------------------------------------------------------------------------------- 1 | /*! modernizr 3.6.0 (Custom Build) | MIT * 2 | * https://modernizr.com/download/?-inputtypes-touchevents-setclasses !*/ 3 | !function(e,t,n){function o(e,t){return typeof e===t}function s(){var e,t,n,s,a,i,l;for(var r in f)if(f.hasOwnProperty(r)){if(e=[],t=f[r],t.name&&(e.push(t.name.toLowerCase()),t.options&&t.options.aliases&&t.options.aliases.length))for(n=0;nr;r++)h.setAttribute("type",o=e[r]),a="text"!==h.type&&"style"in h,a&&(h.value=l,h.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&h.style.WebkitAppearance!==n?(u.appendChild(h),s=t.defaultView,a=s.getComputedStyle&&"textfield"!==s.getComputedStyle(h,null).WebkitAppearance&&0!==h.offsetHeight,u.removeChild(h)):/^(search|tel)$/.test(o)||(a=/^(url|email)$/.test(o)?h.checkValidity&&h.checkValidity()===!1:h.value!=l)),v[e[r]]=!!a;return v}(m);var y=d._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];d._prefixes=y;var g=d.testStyles=r;Modernizr.addTest("touchevents",function(){var n;if("ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch)n=!0;else{var o=["@media (",y.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");g(o,function(e){n=9===e.offsetTop})}return n}),s(),a(c),delete d.addTest,delete d.addAsyncTest;for(var w=0;w"),$container=$el.parent(".pikaday__container");var originalId=$el.attr("id");if(settings.checkIfNativeDate()){var minDate=settings.pikadayOptions.minDate?moment(settings.pikadayOptions.minDate.toISOString()).format("YYYY-MM-DD"):"",maxDate=settings.pikadayOptions.maxDate?moment(settings.pikadayOptions.maxDate.toISOString()).format("YYYY-MM-DD"):"";$input=$(""),originalId&&$input.attr("id",originalId+"-input"),$container.append($input),$display=$(""),$container.append($display),$input.on("change",function(){var val=$(this).val();$display.removeClass("is-empty"),val?(obj.date=moment(val,"YYYY-MM-DD"),obj.value=obj.date.format(settings.outputFormat)):(obj.date=null,obj.value=null,$display.addClass("is-empty")),1*obj.value===parseInt(obj.value,10)&&(obj.value*=1),$el.val(obj.value),obj.date?$display.val(obj.date.format(settings.format)):$display.val(null),$el.trigger("change"),$el.trigger("change-date",[obj])})}else{$input=$(""),originalId&&$input.attr("id",originalId+"-input"),$container.append($input);var hasSelected=!1,selectTimer=null;obj.pikaday=new Pikaday($.extend({},settings.pikadayOptions,{field:$input[0],format:settings.format})),$input.on("change",function(){if(!hasSelected){hasSelected=!0,selectTimer=window.setTimeout(function(){hasSelected=!1},10);var val=$(this).val();$input.removeClass("is-empty"),val?(obj.date=moment(val,settings.format),obj.date.add(settings.dayOffset,"day"),obj.value=obj.date.format(settings.outputFormat),$(this).val(obj.date.format(settings.format))):(obj.date=null,obj.value=null,$input.addClass("is-empty")),1*obj.value===parseInt(obj.value,10)&&(obj.value*=1),$el.val(obj.value),setTimeout(function(){$el.trigger("change"),$el.trigger("change-date",[obj])},1)}})}var setDate=function(date,format){return date?("object"==typeof date&&"function"!=typeof date.format&&(date=moment(date)),"string"==typeof date&&("undefined"!=typeof format&&format||(format=settings.outputFormat),date=moment(date,format)),"number"==typeof date&&(date=moment(date)),obj.pikaday?obj.pikaday.setMoment(date):($input.val(date.format("YYYY-MM-DD")),$input.trigger("change")),date):(obj.pikaday?obj.pikaday.setDate(null):($input.val(null),$input.trigger("change")),null)};return $el.val()&&setDate($el.val()),obj.setDate=setDate,obj}}); -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // 1. All configuration goes here 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | banner: '\n /* \n * PikadayResponsive \n * A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. \n * \n * @author: Francesco Novy \n * @licence: MIT \n * @link https://github.com/mydea/PikadayResponsive \n * @copyright: (c) <%= grunt.template.today("yyyy") %> \n * @version: <%= pkg.version %> \n */ \n\n', 7 | 8 | modernizr: { 9 | dist: { 10 | 'dest': 'dist/dependencies/pikaday-responsive-modernizr.js', 11 | 'tests': [ 12 | 'inputtypes', 13 | 'touchevents' 14 | ], 15 | 'options': [ 16 | 'setClasses' 17 | ], 18 | 'crawl': false, 19 | 'uglify': true 20 | } 21 | }, 22 | cssmin: { 23 | minify: { 24 | files: { 25 | 'dist/pikaday-responsive.min.css': ['dist/pikaday-responsive.css'], 26 | 'dist/pikaday-package.min.css': ['dist/pikaday-package.css'], 27 | 'dist/dependencies/pikaday.min.css': ['dist/dependencies/pikaday.css'] 28 | } 29 | } 30 | }, 31 | uglify: { 32 | options: { 33 | mangle: false 34 | }, 35 | pikadayResponsive: { 36 | options: { 37 | banner: '<%= banner %>' 38 | }, 39 | files: { 40 | 'dist/pikaday-responsive.min.js': ['dist/pikaday-responsive.js'], 41 | 'dist/pikaday-package.min.js': ['dist/pikaday-package.js'] 42 | } 43 | }, 44 | vendor: { 45 | files: { 46 | 'dist/pikaday-responsive.min.js': ['dist/pikaday-responsive.js'], 47 | 'dist/dependencies/pikaday.min.js': ['dist/dependencies/pikaday.js'] 48 | } 49 | } 50 | }, 51 | concat: { 52 | lib: { 53 | options: { 54 | banner: '<%= banner %>' 55 | }, 56 | src: ['src/pikaday-responsive.js'], 57 | dest: 'dist/pikaday-responsive.js' 58 | }, 59 | dependencies: { 60 | files: { 61 | 'dist/dependencies/pikaday.js': ['bower_components/pikaday/pikaday.js'], 62 | 'dist/dependencies/pikaday.css': ['bower_components/pikaday/css/pikaday.css'], 63 | 'dist/dependencies/moment.js': ['bower_components/momentjs/moment.js'], 64 | 'dist/dependencies/moment.min.js': ['bower_components/momentjs/min/moment.min.js'], 65 | 'dist/dependencies/jquery.min.js': ['bower_components/jquery/dist/jquery.min.js'] 66 | } 67 | }, 68 | app: { 69 | options: { 70 | banner: '<%= banner %>' 71 | }, 72 | src: ['dist/dependencies/moment.js', 'dist/dependencies/pikaday.js', 'dist/pikaday-responsive.js'], 73 | dest: 'dist/pikaday-package.js' 74 | }, 75 | css: { 76 | src: ['dist/dependencies/pikaday.css', 'dist/pikaday-responsive.css'], 77 | dest: 'dist/pikaday-package.css' 78 | } 79 | }, 80 | sass: { 81 | dist: { 82 | options: { 83 | style: 'expanded', 84 | sourcemap: 'none' 85 | }, 86 | files: { 87 | 'dist/pikaday-responsive.css': 'src/css/scss/pikaday-responsive.scss' 88 | } 89 | } 90 | }, 91 | 'bower-install-simple': { 92 | options: { 93 | color: true 94 | }, 95 | 'prod': { 96 | options: { 97 | production: true 98 | } 99 | }, 100 | 'dev': { 101 | options: { 102 | production: false 103 | } 104 | } 105 | }, 106 | 'bower-update': { 107 | options: { 108 | pickAll: true, 109 | filter: function(package, options) { 110 | if (package.name.indexOf('jquery') === 0) { 111 | return false 112 | } 113 | return true 114 | }, 115 | rangeChar: '~' 116 | } 117 | } 118 | 119 | }); 120 | 121 | // 3. Where we tell Grunt we plan to use this plug-in. 122 | grunt.loadNpmTasks('grunt-modernizr'); 123 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 124 | grunt.loadNpmTasks('grunt-contrib-uglify'); 125 | grunt.loadNpmTasks('grunt-contrib-concat'); 126 | grunt.loadNpmTasks('grunt-contrib-sass'); 127 | grunt.loadNpmTasks('grunt-bower-install-simple'); 128 | grunt.loadNpmTasks('grunt-bower-update'); 129 | 130 | // 4. Where we tell Grunt what to do when we type 'grunt' into the terminal. 131 | grunt.registerTask('default', ['bower-update', 'bower-install-simple:prod', 'sass', 'concat', 'uglify', 'cssmin', 'modernizr']); 132 | 133 | }; 134 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pikaday Responsive 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |

pikaday-responsive

18 | 19 |

20 | A responsive datepicker built on top of Pikaday. 21 | It shows the native datepicker on mobile devices and a nice JS-picker on desktop. 22 | It is realised as a jQuery-Plugin. 23 |

24 | 25 |

26 | More Infos & Download on GitHub 27 |

28 | 29 |

30 | Created by: Francesco Novy 31 |
www.fnovy.com 32 | | francesconovy@gmail.com 33 |

34 | 35 |

Default values

36 | 37 |

38 | 39 | 40 |

41 | 42 |

The output-value of the input-field is:

43 | 44 |

45 | 46 |

Change output format

47 | 48 |

You can specify an output format as a Moment.js string. E.g. "X" (UNIX timestamp):

49 | 50 |

51 | 52 | 53 |

54 | 55 |

The output-value of the input-field is:

56 | 57 |

58 | 59 |

Change display format

60 | 61 |

You can also specifiy the display format as you wish. E.g. "Do MMM YYYY". You can use the dateObject to implement 62 | clear/today buttons.

63 | 64 |

65 | 66 | 67 | 68 | 69 |

70 | 71 |

The output-value of the input-field is:

72 | 73 |

74 | 75 |

Remote Triggering

76 |

77 | By using labels, you can trigger date pickers from different places on your page. 78 |

79 | 82 | 83 |

Date Picker with min and max date

84 |

85 | By using pikaday option minDate and maxDate you can set up min and max date for native date picker.
86 | NOTE: This feature is only available for Android devices. 87 |

88 |

89 | 90 | 91 |

92 |

The output-value of the input-field is:

93 | 94 |

95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /dist/dependencies/pikaday.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*! 4 | * Pikaday 5 | * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/ 6 | */ 7 | 8 | .pika-single { 9 | z-index: 9999; 10 | display: block; 11 | position: relative; 12 | color: #333; 13 | background: #fff; 14 | border: 1px solid #ccc; 15 | border-bottom-color: #bbb; 16 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 17 | } 18 | 19 | /* 20 | clear child float (pika-lendar), using the famous micro clearfix hack 21 | http://nicolasgallagher.com/micro-clearfix-hack/ 22 | */ 23 | .pika-single:before, 24 | .pika-single:after { 25 | content: " "; 26 | display: table; 27 | } 28 | .pika-single:after { clear: both } 29 | .pika-single { *zoom: 1 } 30 | 31 | .pika-single.is-hidden { 32 | display: none; 33 | } 34 | 35 | .pika-single.is-bound { 36 | position: absolute; 37 | box-shadow: 0 5px 15px -5px rgba(0,0,0,.5); 38 | } 39 | 40 | .pika-lendar { 41 | float: left; 42 | width: 240px; 43 | margin: 8px; 44 | } 45 | 46 | .pika-title { 47 | position: relative; 48 | text-align: center; 49 | } 50 | 51 | .pika-label { 52 | display: inline-block; 53 | *display: inline; 54 | position: relative; 55 | z-index: 9999; 56 | overflow: hidden; 57 | margin: 0; 58 | padding: 5px 3px; 59 | font-size: 14px; 60 | line-height: 20px; 61 | font-weight: bold; 62 | background-color: #fff; 63 | } 64 | .pika-title select { 65 | cursor: pointer; 66 | position: absolute; 67 | z-index: 9998; 68 | margin: 0; 69 | left: 0; 70 | top: 5px; 71 | filter: alpha(opacity=0); 72 | opacity: 0; 73 | } 74 | 75 | .pika-prev, 76 | .pika-next { 77 | display: block; 78 | cursor: pointer; 79 | position: relative; 80 | outline: none; 81 | border: 0; 82 | padding: 0; 83 | width: 20px; 84 | height: 30px; 85 | /* hide text using text-indent trick, using width value (it's enough) */ 86 | text-indent: 20px; 87 | white-space: nowrap; 88 | overflow: hidden; 89 | background-color: transparent; 90 | background-position: center center; 91 | background-repeat: no-repeat; 92 | background-size: 75% 75%; 93 | opacity: .5; 94 | *position: absolute; 95 | *top: 0; 96 | } 97 | 98 | .pika-prev:hover, 99 | .pika-next:hover { 100 | opacity: 1; 101 | } 102 | 103 | .pika-prev, 104 | .is-rtl .pika-next { 105 | float: left; 106 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=='); 107 | *left: 0; 108 | } 109 | 110 | .pika-next, 111 | .is-rtl .pika-prev { 112 | float: right; 113 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII='); 114 | *right: 0; 115 | } 116 | 117 | .pika-prev.is-disabled, 118 | .pika-next.is-disabled { 119 | cursor: default; 120 | opacity: .2; 121 | } 122 | 123 | .pika-select { 124 | display: inline-block; 125 | *display: inline; 126 | } 127 | 128 | .pika-table { 129 | width: 100%; 130 | border-collapse: collapse; 131 | border-spacing: 0; 132 | border: 0; 133 | } 134 | 135 | .pika-table th, 136 | .pika-table td { 137 | width: 14.285714285714286%; 138 | padding: 0; 139 | } 140 | 141 | .pika-table th { 142 | color: #999; 143 | font-size: 12px; 144 | line-height: 25px; 145 | font-weight: bold; 146 | text-align: center; 147 | } 148 | 149 | .pika-button { 150 | cursor: pointer; 151 | display: block; 152 | box-sizing: border-box; 153 | -moz-box-sizing: border-box; 154 | outline: none; 155 | border: 0; 156 | margin: 0; 157 | width: 100%; 158 | padding: 5px; 159 | color: #666; 160 | font-size: 12px; 161 | line-height: 15px; 162 | text-align: right; 163 | background: #f5f5f5; 164 | } 165 | 166 | .pika-week { 167 | font-size: 11px; 168 | color: #999; 169 | } 170 | 171 | .is-today .pika-button { 172 | color: #33aaff; 173 | font-weight: bold; 174 | } 175 | 176 | .is-selected .pika-button, 177 | .has-event .pika-button { 178 | color: #fff; 179 | font-weight: bold; 180 | background: #33aaff; 181 | box-shadow: inset 0 1px 3px #178fe5; 182 | border-radius: 3px; 183 | } 184 | 185 | .has-event .pika-button { 186 | background: #005da9; 187 | box-shadow: inset 0 1px 3px #0076c9; 188 | } 189 | 190 | .is-disabled .pika-button, 191 | .is-inrange .pika-button { 192 | background: #D5E9F7; 193 | } 194 | 195 | .is-startrange .pika-button { 196 | color: #fff; 197 | background: #6CB31D; 198 | box-shadow: none; 199 | border-radius: 3px; 200 | } 201 | 202 | .is-endrange .pika-button { 203 | color: #fff; 204 | background: #33aaff; 205 | box-shadow: none; 206 | border-radius: 3px; 207 | } 208 | 209 | .is-disabled .pika-button { 210 | pointer-events: none; 211 | cursor: default; 212 | color: #999; 213 | opacity: .3; 214 | } 215 | 216 | .is-outside-current-month .pika-button { 217 | color: #999; 218 | opacity: .3; 219 | } 220 | 221 | .is-selection-disabled { 222 | pointer-events: none; 223 | cursor: default; 224 | } 225 | 226 | .pika-button:hover, 227 | .pika-row.pick-whole-week:hover .pika-button { 228 | color: #fff; 229 | background: #ff8000; 230 | box-shadow: none; 231 | border-radius: 3px; 232 | } 233 | 234 | /* styling for abbr */ 235 | .pika-table abbr { 236 | border-bottom: none; 237 | cursor: help; 238 | } 239 | 240 | -------------------------------------------------------------------------------- /dist/pikaday-package.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*! 4 | * Pikaday 5 | * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/ 6 | */ 7 | 8 | .pika-single { 9 | z-index: 9999; 10 | display: block; 11 | position: relative; 12 | color: #333; 13 | background: #fff; 14 | border: 1px solid #ccc; 15 | border-bottom-color: #bbb; 16 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 17 | } 18 | 19 | /* 20 | clear child float (pika-lendar), using the famous micro clearfix hack 21 | http://nicolasgallagher.com/micro-clearfix-hack/ 22 | */ 23 | .pika-single:before, 24 | .pika-single:after { 25 | content: " "; 26 | display: table; 27 | } 28 | .pika-single:after { clear: both } 29 | .pika-single { *zoom: 1 } 30 | 31 | .pika-single.is-hidden { 32 | display: none; 33 | } 34 | 35 | .pika-single.is-bound { 36 | position: absolute; 37 | box-shadow: 0 5px 15px -5px rgba(0,0,0,.5); 38 | } 39 | 40 | .pika-lendar { 41 | float: left; 42 | width: 240px; 43 | margin: 8px; 44 | } 45 | 46 | .pika-title { 47 | position: relative; 48 | text-align: center; 49 | } 50 | 51 | .pika-label { 52 | display: inline-block; 53 | *display: inline; 54 | position: relative; 55 | z-index: 9999; 56 | overflow: hidden; 57 | margin: 0; 58 | padding: 5px 3px; 59 | font-size: 14px; 60 | line-height: 20px; 61 | font-weight: bold; 62 | background-color: #fff; 63 | } 64 | .pika-title select { 65 | cursor: pointer; 66 | position: absolute; 67 | z-index: 9998; 68 | margin: 0; 69 | left: 0; 70 | top: 5px; 71 | filter: alpha(opacity=0); 72 | opacity: 0; 73 | } 74 | 75 | .pika-prev, 76 | .pika-next { 77 | display: block; 78 | cursor: pointer; 79 | position: relative; 80 | outline: none; 81 | border: 0; 82 | padding: 0; 83 | width: 20px; 84 | height: 30px; 85 | /* hide text using text-indent trick, using width value (it's enough) */ 86 | text-indent: 20px; 87 | white-space: nowrap; 88 | overflow: hidden; 89 | background-color: transparent; 90 | background-position: center center; 91 | background-repeat: no-repeat; 92 | background-size: 75% 75%; 93 | opacity: .5; 94 | *position: absolute; 95 | *top: 0; 96 | } 97 | 98 | .pika-prev:hover, 99 | .pika-next:hover { 100 | opacity: 1; 101 | } 102 | 103 | .pika-prev, 104 | .is-rtl .pika-next { 105 | float: left; 106 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=='); 107 | *left: 0; 108 | } 109 | 110 | .pika-next, 111 | .is-rtl .pika-prev { 112 | float: right; 113 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII='); 114 | *right: 0; 115 | } 116 | 117 | .pika-prev.is-disabled, 118 | .pika-next.is-disabled { 119 | cursor: default; 120 | opacity: .2; 121 | } 122 | 123 | .pika-select { 124 | display: inline-block; 125 | *display: inline; 126 | } 127 | 128 | .pika-table { 129 | width: 100%; 130 | border-collapse: collapse; 131 | border-spacing: 0; 132 | border: 0; 133 | } 134 | 135 | .pika-table th, 136 | .pika-table td { 137 | width: 14.285714285714286%; 138 | padding: 0; 139 | } 140 | 141 | .pika-table th { 142 | color: #999; 143 | font-size: 12px; 144 | line-height: 25px; 145 | font-weight: bold; 146 | text-align: center; 147 | } 148 | 149 | .pika-button { 150 | cursor: pointer; 151 | display: block; 152 | box-sizing: border-box; 153 | -moz-box-sizing: border-box; 154 | outline: none; 155 | border: 0; 156 | margin: 0; 157 | width: 100%; 158 | padding: 5px; 159 | color: #666; 160 | font-size: 12px; 161 | line-height: 15px; 162 | text-align: right; 163 | background: #f5f5f5; 164 | } 165 | 166 | .pika-week { 167 | font-size: 11px; 168 | color: #999; 169 | } 170 | 171 | .is-today .pika-button { 172 | color: #33aaff; 173 | font-weight: bold; 174 | } 175 | 176 | .is-selected .pika-button, 177 | .has-event .pika-button { 178 | color: #fff; 179 | font-weight: bold; 180 | background: #33aaff; 181 | box-shadow: inset 0 1px 3px #178fe5; 182 | border-radius: 3px; 183 | } 184 | 185 | .has-event .pika-button { 186 | background: #005da9; 187 | box-shadow: inset 0 1px 3px #0076c9; 188 | } 189 | 190 | .is-disabled .pika-button, 191 | .is-inrange .pika-button { 192 | background: #D5E9F7; 193 | } 194 | 195 | .is-startrange .pika-button { 196 | color: #fff; 197 | background: #6CB31D; 198 | box-shadow: none; 199 | border-radius: 3px; 200 | } 201 | 202 | .is-endrange .pika-button { 203 | color: #fff; 204 | background: #33aaff; 205 | box-shadow: none; 206 | border-radius: 3px; 207 | } 208 | 209 | .is-disabled .pika-button { 210 | pointer-events: none; 211 | cursor: default; 212 | color: #999; 213 | opacity: .3; 214 | } 215 | 216 | .is-outside-current-month .pika-button { 217 | color: #999; 218 | opacity: .3; 219 | } 220 | 221 | .is-selection-disabled { 222 | pointer-events: none; 223 | cursor: default; 224 | } 225 | 226 | .pika-button:hover, 227 | .pika-row.pick-whole-week:hover .pika-button { 228 | color: #fff; 229 | background: #ff8000; 230 | box-shadow: none; 231 | border-radius: 3px; 232 | } 233 | 234 | /* styling for abbr */ 235 | .pika-table abbr { 236 | border-bottom: none; 237 | cursor: help; 238 | } 239 | 240 | 241 | /* 242 | * PikadayResponsive 243 | * A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. 244 | * 245 | * @author: Francesco Novy 246 | * @licence: MIT 247 | * @link https://github.com/mydea/PikadayResponsive 248 | * @copyright: (c) 2016 249 | * @version: 0.6.7 250 | */ 251 | .pikaday__container { 252 | display: inline-block; 253 | position: relative; 254 | } 255 | 256 | /* Height and width has to be equal! */ 257 | .pikaday__display, .pikaday__invisible { 258 | width: 100%; 259 | } 260 | 261 | .pikaday__display--native { 262 | pointer-events: none; 263 | cursor: pointer; 264 | } 265 | 266 | .pikaday__display.is-invalid { 267 | background: rgba(255, 0, 0, 0.05); 268 | } 269 | 270 | .pikaday__invisible { 271 | opacity: 0; 272 | color: transparent; 273 | background: transparent; 274 | border: none; 275 | box-shadow: none; 276 | position: absolute; 277 | display: block; 278 | left: 0; 279 | top: 0; 280 | height: 100%; 281 | width: 100%; 282 | } 283 | 284 | /*# sourceMappingURL=dist/pikaday-responsive.css.map */ 285 | -------------------------------------------------------------------------------- /src/pikaday-responsive.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof define === 'function') { 3 | define('pikaday-responsive', ['exports'], function(exports) { 4 | return (exports['default'] = factory()); 5 | }); 6 | } else if (typeof module === 'object') { 7 | module.exports = factory(); 8 | } else { 9 | root.pikadayResponsive = factory(); 10 | } 11 | }(this, function() { 12 | 13 | // Check if all dependencies are loaded 14 | if (!moment) { 15 | console.error("You need to load moment.js in order to use pikaday-responsive."); 16 | return; 17 | } 18 | 19 | if (!jQuery) { 20 | console.error("You need to load jQuery in order to use pikaday-responsive."); 21 | return; 22 | } 23 | 24 | if (!Pikaday) { 25 | console.error("You need to load pikaday in order to use pikaday-responsive."); 26 | return; 27 | } 28 | 29 | var defaultOptions = { 30 | format: "YYYY-MM-DD", 31 | outputFormat: "YYYY-MM-DD", 32 | checkIfNativeDate: function() { 33 | return Modernizr.inputtypes.date && (Modernizr.touchevents && navigator.appVersion.indexOf("Win") === -1); 34 | }, 35 | classes: "", 36 | placeholder: "Select a date", 37 | pikadayOptions: {}, 38 | dayOffset: 0, 39 | attributes: "" 40 | }; 41 | 42 | return function(el, options) { 43 | var $el = $(el); 44 | var settings = $.extend({}, defaultOptions, options); 45 | 46 | // The container element for the input 47 | var $container; 48 | // The actual input field 49 | var $input; 50 | // The display input field 51 | var $display; 52 | // The actual output value 53 | var obj = { 54 | pikaday: null, 55 | value: null, 56 | date: null, 57 | element: $el[0] 58 | }; 59 | 60 | // Check if first param is 61 | if (!$el.length || $el[0].tagName !== "INPUT") { 62 | console.error("pikadayResponsive expects an input-field as its first element.", $el[0]); 63 | return false; 64 | } 65 | 66 | // The original input field is made hidden. This field will contain the actual value. 67 | $el.attr("type", "hidden"); 68 | // Wrap the input in a container 69 | $el.wrap(""); 70 | $container = $el.parent(".pikaday__container"); 71 | 72 | // If the original input has an ID, use it to generate IDs for the generated display inputs 73 | var originalId = $el.attr('id'); 74 | 75 | if (settings.checkIfNativeDate()) { 76 | // Use native date picker 77 | var minDate = settings.pikadayOptions.minDate ? moment(settings.pikadayOptions.minDate.toISOString()).format("YYYY-MM-DD") : ""; 78 | var maxDate = settings.pikadayOptions.maxDate ? moment(settings.pikadayOptions.maxDate.toISOString()).format("YYYY-MM-DD") : ""; 79 | $input = $(""); 80 | if (originalId) { 81 | $input.attr('id', originalId + '-input'); 82 | } 83 | $container.append($input); 84 | 85 | $display = $(""); 86 | $container.append($display); 87 | 88 | $input.on("change", function() { 89 | var val = $(this).val(); 90 | $display.removeClass("is-empty"); 91 | 92 | if (!val) { 93 | obj.date = null; 94 | obj.value = null; 95 | $display.addClass("is-empty"); 96 | } else { 97 | obj.date = moment(val, "YYYY-MM-DD"); 98 | obj.value = obj.date.format(settings.outputFormat); 99 | } 100 | 101 | // Convert numbers (unix timestamp) to ints 102 | if (obj.value * 1 === parseInt(obj.value, 10)) { 103 | obj.value *= 1; 104 | } 105 | $el.val(obj.value); 106 | if (obj.date) { 107 | $display.val(obj.date.format(settings.format)); 108 | } else { 109 | $display.val(null); 110 | } 111 | 112 | 113 | $el.trigger("change"); 114 | $el.trigger("change-date", [obj]); 115 | }); 116 | 117 | } else { 118 | // Use Pikaday 119 | $input = $(""); 120 | if (originalId) { 121 | $input.attr('id', originalId + '-input'); 122 | } 123 | $container.append($input); 124 | 125 | var hasSelected = false; 126 | var selectTimer = null; 127 | 128 | obj.pikaday = new Pikaday($.extend({}, settings.pikadayOptions, { 129 | field: $input[0], 130 | format: settings.format, 131 | })); 132 | 133 | $input.on("change", function() { 134 | if (hasSelected) { 135 | return; 136 | } 137 | 138 | hasSelected = true; 139 | selectTimer = window.setTimeout(function() { 140 | hasSelected = false; 141 | }, 10); 142 | 143 | var val = $(this).val(); 144 | $input.removeClass("is-empty"); 145 | 146 | if (!val) { 147 | obj.date = null; 148 | obj.value = null; 149 | $input.addClass("is-empty") 150 | } else { 151 | obj.date = moment(val, settings.format); 152 | // Add an optional day offset to account for time zones 153 | obj.date.add(settings.dayOffset, "day"); 154 | 155 | obj.value = obj.date.format(settings.outputFormat); 156 | $(this).val(obj.date.format(settings.format)); 157 | } 158 | 159 | // Convert numbers (unix timestamp) to ints 160 | if (obj.value * 1 === parseInt(obj.value, 10)) { 161 | obj.value *= 1; 162 | } 163 | $el.val(obj.value); 164 | 165 | // Wait 1ms in order to circumvent bug where events weren't triggered 166 | setTimeout(function() { 167 | $el.trigger("change"); 168 | $el.trigger("change-date", [obj]); 169 | }, 1); 170 | }); 171 | } 172 | 173 | /** 174 | * This function sets the date to a specific value. 175 | * 176 | * @method setDate 177 | * @param date It is preferred to give a moment-object as param, but vanilla Dates or strings in the outputFormat work too 178 | * @returns Object The moment-date that was used to set the date 179 | */ 180 | var setDate = function(date, format) { 181 | // If date is null, reset the field 182 | if (!date) { 183 | if (obj.pikaday) { 184 | obj.pikaday.setDate(null); 185 | } else { 186 | $input.val(null); 187 | $input.trigger("change"); 188 | } 189 | 190 | return null; 191 | } 192 | 193 | // Format date into moment-date 194 | if (typeof date === "object" && typeof date.format !== "function") { 195 | date = moment(date); 196 | } 197 | if (typeof date === "string") { 198 | if (typeof format === "undefined" || !format) { 199 | format = settings.outputFormat; 200 | } 201 | date = moment(date, format); 202 | } 203 | if (typeof date === "number") { 204 | date = moment(date); 205 | } 206 | 207 | if (obj.pikaday) { 208 | obj.pikaday.setMoment(date); 209 | } else { 210 | $input.val(date.format("YYYY-MM-DD")); 211 | $input.trigger("change"); 212 | } 213 | 214 | return date; 215 | }; 216 | 217 | if ($el.val()) { 218 | setDate($el.val()); 219 | } 220 | 221 | obj.setDate = setDate; 222 | 223 | return obj; 224 | } 225 | })); 226 | -------------------------------------------------------------------------------- /dist/pikaday-responsive.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PikadayResponsive 4 | * A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. 5 | * 6 | * @author: Francesco Novy 7 | * @licence: MIT 8 | * @link https://github.com/mydea/PikadayResponsive 9 | * @copyright: (c) 2018 10 | * @version: 0.7.0 11 | */ 12 | 13 | (function(root, factory) { 14 | if (typeof define === 'function') { 15 | define('pikaday-responsive', ['exports'], function(exports) { 16 | return (exports['default'] = factory()); 17 | }); 18 | } else if (typeof module === 'object') { 19 | module.exports = factory(); 20 | } else { 21 | root.pikadayResponsive = factory(); 22 | } 23 | }(this, function() { 24 | 25 | // Check if all dependencies are loaded 26 | if (!moment) { 27 | console.error("You need to load moment.js in order to use pikaday-responsive."); 28 | return; 29 | } 30 | 31 | if (!jQuery) { 32 | console.error("You need to load jQuery in order to use pikaday-responsive."); 33 | return; 34 | } 35 | 36 | if (!Pikaday) { 37 | console.error("You need to load pikaday in order to use pikaday-responsive."); 38 | return; 39 | } 40 | 41 | var defaultOptions = { 42 | format: "YYYY-MM-DD", 43 | outputFormat: "YYYY-MM-DD", 44 | checkIfNativeDate: function() { 45 | return Modernizr.inputtypes.date && (Modernizr.touchevents && navigator.appVersion.indexOf("Win") === -1); 46 | }, 47 | classes: "", 48 | placeholder: "Select a date", 49 | pikadayOptions: {}, 50 | dayOffset: 0, 51 | attributes: "" 52 | }; 53 | 54 | return function(el, options) { 55 | var $el = $(el); 56 | var settings = $.extend({}, defaultOptions, options); 57 | 58 | // The container element for the input 59 | var $container; 60 | // The actual input field 61 | var $input; 62 | // The display input field 63 | var $display; 64 | // The actual output value 65 | var obj = { 66 | pikaday: null, 67 | value: null, 68 | date: null, 69 | element: $el[0] 70 | }; 71 | 72 | // Check if first param is 73 | if (!$el.length || $el[0].tagName !== "INPUT") { 74 | console.error("pikadayResponsive expects an input-field as its first element.", $el[0]); 75 | return false; 76 | } 77 | 78 | // The original input field is made hidden. This field will contain the actual value. 79 | $el.attr("type", "hidden"); 80 | // Wrap the input in a container 81 | $el.wrap(""); 82 | $container = $el.parent(".pikaday__container"); 83 | 84 | // If the original input has an ID, use it to generate IDs for the generated display inputs 85 | var originalId = $el.attr('id'); 86 | 87 | if (settings.checkIfNativeDate()) { 88 | // Use native date picker 89 | var minDate = settings.pikadayOptions.minDate ? moment(settings.pikadayOptions.minDate.toISOString()).format("YYYY-MM-DD") : ""; 90 | var maxDate = settings.pikadayOptions.maxDate ? moment(settings.pikadayOptions.maxDate.toISOString()).format("YYYY-MM-DD") : ""; 91 | $input = $(""); 92 | if (originalId) { 93 | $input.attr('id', originalId + '-input'); 94 | } 95 | $container.append($input); 96 | 97 | $display = $(""); 98 | $container.append($display); 99 | 100 | $input.on("change", function() { 101 | var val = $(this).val(); 102 | $display.removeClass("is-empty"); 103 | 104 | if (!val) { 105 | obj.date = null; 106 | obj.value = null; 107 | $display.addClass("is-empty"); 108 | } else { 109 | obj.date = moment(val, "YYYY-MM-DD"); 110 | obj.value = obj.date.format(settings.outputFormat); 111 | } 112 | 113 | // Convert numbers (unix timestamp) to ints 114 | if (obj.value * 1 === parseInt(obj.value, 10)) { 115 | obj.value *= 1; 116 | } 117 | $el.val(obj.value); 118 | if (obj.date) { 119 | $display.val(obj.date.format(settings.format)); 120 | } else { 121 | $display.val(null); 122 | } 123 | 124 | 125 | $el.trigger("change"); 126 | $el.trigger("change-date", [obj]); 127 | }); 128 | 129 | } else { 130 | // Use Pikaday 131 | $input = $(""); 132 | if (originalId) { 133 | $input.attr('id', originalId + '-input'); 134 | } 135 | $container.append($input); 136 | 137 | var hasSelected = false; 138 | var selectTimer = null; 139 | 140 | obj.pikaday = new Pikaday($.extend({}, settings.pikadayOptions, { 141 | field: $input[0], 142 | format: settings.format, 143 | })); 144 | 145 | $input.on("change", function() { 146 | if (hasSelected) { 147 | return; 148 | } 149 | 150 | hasSelected = true; 151 | selectTimer = window.setTimeout(function() { 152 | hasSelected = false; 153 | }, 10); 154 | 155 | var val = $(this).val(); 156 | $input.removeClass("is-empty"); 157 | 158 | if (!val) { 159 | obj.date = null; 160 | obj.value = null; 161 | $input.addClass("is-empty") 162 | } else { 163 | obj.date = moment(val, settings.format); 164 | // Add an optional day offset to account for time zones 165 | obj.date.add(settings.dayOffset, "day"); 166 | 167 | obj.value = obj.date.format(settings.outputFormat); 168 | $(this).val(obj.date.format(settings.format)); 169 | } 170 | 171 | // Convert numbers (unix timestamp) to ints 172 | if (obj.value * 1 === parseInt(obj.value, 10)) { 173 | obj.value *= 1; 174 | } 175 | $el.val(obj.value); 176 | 177 | // Wait 1ms in order to circumvent bug where events weren't triggered 178 | setTimeout(function() { 179 | $el.trigger("change"); 180 | $el.trigger("change-date", [obj]); 181 | }, 1); 182 | }); 183 | } 184 | 185 | /** 186 | * This function sets the date to a specific value. 187 | * 188 | * @method setDate 189 | * @param date It is preferred to give a moment-object as param, but vanilla Dates or strings in the outputFormat work too 190 | * @returns Object The moment-date that was used to set the date 191 | */ 192 | var setDate = function(date, format) { 193 | // If date is null, reset the field 194 | if (!date) { 195 | if (obj.pikaday) { 196 | obj.pikaday.setDate(null); 197 | } else { 198 | $input.val(null); 199 | $input.trigger("change"); 200 | } 201 | 202 | return null; 203 | } 204 | 205 | // Format date into moment-date 206 | if (typeof date === "object" && typeof date.format !== "function") { 207 | date = moment(date); 208 | } 209 | if (typeof date === "string") { 210 | if (typeof format === "undefined" || !format) { 211 | format = settings.outputFormat; 212 | } 213 | date = moment(date, format); 214 | } 215 | if (typeof date === "number") { 216 | date = moment(date); 217 | } 218 | 219 | if (obj.pikaday) { 220 | obj.pikaday.setMoment(date); 221 | } else { 222 | $input.val(date.format("YYYY-MM-DD")); 223 | $input.trigger("change"); 224 | } 225 | 226 | return date; 227 | }; 228 | 229 | if ($el.val()) { 230 | setDate($el.val()); 231 | } 232 | 233 | obj.setDate = setDate; 234 | 235 | return obj; 236 | } 237 | })); 238 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pikaday Responsive 2 | ================= 3 | 4 | A responsive datepicker built on top of Pikaday. It shows the native datepicker on mobile devices and a nice JS-picker on desktop. It is realised as a jQuery-Plugin. 5 | 6 | Pikaday is a nice and lean datepicker. For more details, see here: https://github.com/dbushell/Pikaday 7 | 8 | Try the demo: https://mydea.github.io/PikadayResponsive/ 9 | 10 | Try it on mobile, too: 11 | 12 | ![PikadayResponsive Demo](https://api.qrserver.com/v1/create-qr-code/?data=https%3A%2F%2Fmydea.github.io%2FPikadayResponsive%2F&size=220x220&margin=0) 13 | 14 | ## Why? 15 | Pikaday is a great datepicker, and there are a lot of other datepickers out there that work really well. However, all of them fall short when used on a mobile device, where the native datepickers work best (because they have been specifically optimised for the mobile experience). Native Datepickers have some drawbacks, though: 16 | 17 | * The output format cannot be customized 18 | * While they work quite reliably on mobile devices, support on desktop devices is either non-existing (Firefox) or their UX is rather terrible (Chrome). 19 | 20 | PikadayResponsive tries to solve this problem. 21 | 22 | ## How it works 23 | Basically, PikadayResponsive tries to detect if you are on a mobile device. For this, Modernizr is used (altough you can choose to use a different feature detection library if you want). It checks for touch and HTML5 input-type date support, and if one of them is missing, it simply displays a Pikaday-datepicker. 24 | 25 | If, however, touch AND HTML5-date support are detected, it will instead display a native input type="date". Over this native input field, another, readonly and click-through input field is displayed, in which a formatted date is displayed. 26 | 27 | ## Dependencies 28 | PikadayResponsive needs the following components to work: 29 | 30 | * jQuery 31 | * Moment.js (for date formatting) 32 | * Pikaday (https://github.com/dbushell/Pikaday) 33 | * Modernizr or other feature detection library 34 | 35 | You can also use pikaday-package.js, which bundles Moment.js, Pikaday, and Pikaday Responsive. It does not, however, contain jQuery and Modernizr - you have to add them manually. The dist/ directory contains the package files, a directory package_components/ which contains the individual files the package bundles, and a directory dependencies/ which contains jQuery and an automatically generated copy of modernizr pared down to only the features PikayResponsive requires. 36 | 37 | To build a fresh copy of the package, with the latest version of the dependencies, simply download or clone the directory, and run `npm install` followed by `grunt`. 38 | 39 | ## Usage 40 | You can install PikadayResponsive via Bower: 41 | 42 | ```console 43 | bower install pikaday-responsive --save 44 | ``` 45 | 46 | You will need to include the following scripts at the bottom of your site: 47 | 48 | ```html 49 | 50 | 51 | ``` 52 | 53 | or alternatively: 54 | 55 | ```html 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | And in the head-section 63 | 64 | ```html 65 | 66 | 67 | ``` 68 | 69 | As mentioned above, you can also use head.js or another feature detection library instead of Moderizr. 70 | 71 | The CSS-file contains the basic styles for the Pikaday-Datepicker. You can change/overwrite them as you wish. 72 | 73 | To use it, call the following on an input field: 74 | 75 | HTML: 76 | 77 | ```html 78 | 79 | ``` 80 | 81 | JS: 82 | 83 | ```js 84 | var dateObject = pikadayResponsive(document.getElementById("date")); 85 | ``` 86 | 87 | The `dateObject` returned from `pikadayResponsive()` is a POJO (plain old JavaScript object) with the following properties: 88 | 89 | ```javascript 90 | dateObject = { 91 | pikaday: null, // This is either null (if native date is used) or the Pikaday-instance 92 | element: input#date1, // The original element 93 | value: "2015-10-20", // The current value of the input. This is the date formatted with outputFormat 94 | date: Object, // The Moment.js-Date-Object of the current value 95 | } 96 | ``` 97 | 98 | 99 | ## Setting the date: instance.setDate(newDate) 100 | 101 | In addition, the dateObject has a function: `setDate()`. This function takes either a Moment.js object, a native JS-Date or a string in the specified `outputFormat`. 102 | Optionally, you can also pass in a format to parse from as second parameter: `instance.setDate(newDate, "YYYY-MM-DD")`. 103 | The input will be set to this value (no matter if it is a native date picker or a Pikaday picker) and a change-event will be triggered. 104 | If you call `dateObject.setDate(null)`, the field will be cleared. If you want to set the picker to the current day, you can simply call `dateObject.setDate(moment())`. 105 | 106 | Whenever the input changes, the value on the original input will be updated as well and a regular `change` event will be triggered. 107 | Additonally, a `change-date` event will be triggered on the original input. This event receives the dateObject as its second parameter: 108 | 109 | ```js 110 | var el = document.getElementById("date1"); 111 | var dateObject = pikadayResponsive(el); 112 | $(el).on("change-date", function(e, dateObject) { 113 | // do something with the dateObject 114 | }); 115 | ``` 116 | 117 | Note that the date object is always the same object and that it will automatically update its value/date: 118 | 119 | ```js 120 | var el = document.getElementById("date1"); 121 | var dateObject = pikadayResponsive(el); 122 | console.log(dateObject.value); // null 123 | 124 | // The user enters "2015-10-5" in the input field 125 | console.log(dateObject.value); // 2015-10-5 126 | ``` 127 | 128 | Note that the fields will not be updated automatically if you change the value programmatically. 129 | You have to update the date with `setDate()` in order for it to take effect. 130 | 131 | ## Configuration 132 | There are some options to configure PikadayResponsive. Following are the default-values: 133 | 134 | ```js 135 | pikadayResponsive(document.getElementById("date1"), { 136 | format: "DD.MM.YYYY", 137 | outputFormat: "x", 138 | checkIfNativeDate: function() { 139 | // return true if native date field should be used 140 | }, 141 | placeholder: "", 142 | classes: "", 143 | dayOffset: 0, 144 | pikadayOptions: {} 145 | }); 146 | ``` 147 | 148 | ### format 149 | Determines how a date will be displayed in the input-field. Has to be a Moment.js format-string, like `DD.MM.YYYY`. 150 | See [Moment.js Docs](http://momentjs.com/docs/#/displaying/) for all available options. 151 | 152 | ### outputFormat 153 | Determines the output of the field. Basically, this is what you will get if you call `$("#date").val()` or if you submit the form. has to be a Moment.js format-string, like "YYYY-MM-DD". 154 | See [Moment.js Docs](http://momentjs.com/docs/#/displaying/) for all available options. 155 | 156 | ### placeholder 157 | The placeholder for the input-field. 158 | 159 | ### classes 160 | A string with classes that should be added to the displayed input-fields 161 | 162 | ### dayOffset 163 | A number which is added to the date in Pikaday-mode. This is especially useful when working with timezones. 164 | If you set a default timezone with moment-timezone, like `moment.tz.setDefault("America/Los_Angeles")`, your dates may turn out wrong if your browser's timezone is different. This is caused if the specified timezone's offset is lower than your browser's timezone offset. 165 | In this case, the picked date, which might be "2016-04-12 00:00:00" in "America/Los_Angeles", is converted to your local time zone, e.g. to "2016-04-11 22:00:00". This can be very hard to work with. 166 | The number of days specified here will simply be added to the selected date before it is processed. In the above case, the date that would actually be returned as value would be "2016-04-12 22:00:00". 167 | 168 | An example implementation would be: 169 | 170 | ```js 171 | var defaultOffset = moment.tz("Europe/Vienna")._offset; // This is your client's timezone 172 | var currentOffset = moment()._offset; // This is your specified timezone 173 | 174 | var dayOffset = 0; 175 | if(currentOffset < defaultOffset) { 176 | dayOffset = 1; 177 | } 178 | 179 | var $date1 = $("#date1"); 180 | var instance1 = pikadayResponsive($date1, { 181 | dayOffset: dayOffset 182 | }); 183 | ``` 184 | 185 | ### checkIfNativeDate 186 | You can overwrite this, for example if you don't want to use Modernizr. This has to be a function. 187 | If this function returns true, the native date picker will be used, otherwise Pikaday will be used. 188 | By default, the following function is used: 189 | 190 | ```js 191 | function () { 192 | return Modernizr.inputtypes.date && (Modernizr.touch && navigator.appVersion.indexOf("Win") === -1); 193 | } 194 | ``` 195 | 196 | ### pikadayOptions 197 | An object with options that will be used to initialize Pikaday. Note that ```field``` and ```format``` will be overridden. 198 | 199 | ## Manually opening the date picker 200 | 201 | Opening the date picker from somewhere else on the page can be a bit tricky. 202 | The best way to do this is by simply using standard HTML labels. 203 | Since 0.7.0, the generated inputs will receive an ID, if the original input had an ID. It will be the original ID + `-input`. 204 | For example: 205 | 206 | ```html 207 | 208 | ``` 209 | 210 | will become: 211 | 212 | ```html 213 | 214 | ``` 215 | 216 | This makes it possible to trigger the date picker from any place on your page with a simple label: 217 | 218 | ```html 219 | 220 | ``` 221 | 222 | See an example for this on the bottom of the [Demo](https://mydea.github.io/PikadayResponsive/). 223 | 224 | ## Changelog 225 | 226 | For changes, see the [Changelog](CHANGELOG.md) 227 | 228 | Additionally, please note the breaking changes from 0.5 to 0.6: [Information about upgrading to >= 0.6.0](upgrading-to-v0-6.md) 229 | 230 | ## Author 231 | PikadayResponsive has been created by Francesco Novy | http://www.fnovy.com | francesconovy@gmail.com | @_fnovy 232 | 233 | ## Credits 234 | Credits go to David Bushell and Ramiro Rikkert for creating Pikaday. 235 | 236 | * David Bushell http://dbushell.com @dbushell 237 | * Ramiro Rikkert GitHub @RamRik 238 | 239 | ## Copyright 240 | Copyright © 2016 Francesco Novy | MIT license 241 | -------------------------------------------------------------------------------- /dist/dependencies/pikaday.min.js: -------------------------------------------------------------------------------- 1 | !function(root,factory){"use strict";var moment;if("object"==typeof exports){try{moment=require("moment")}catch(e){}module.exports=factory(moment)}else"function"==typeof define&&define.amd?define(function(req){var id="moment";try{moment=req(id)}catch(e){}return factory(moment)}):root.Pikaday=factory(root.moment)}(this,function(moment){"use strict";var hasMoment="function"==typeof moment,hasEventListeners=!!window.addEventListener,document=window.document,sto=window.setTimeout,addEvent=function(el,e,callback,capture){hasEventListeners?el.addEventListener(e,callback,!!capture):el.attachEvent("on"+e,callback)},removeEvent=function(el,e,callback,capture){hasEventListeners?el.removeEventListener(e,callback,!!capture):el.detachEvent("on"+e,callback)},trim=function(str){return str.trim?str.trim():str.replace(/^\s+|\s+$/g,"")},hasClass=function(el,cn){return(" "+el.className+" ").indexOf(" "+cn+" ")!==-1},addClass=function(el,cn){hasClass(el,cn)||(el.className=""===el.className?cn:el.className+" "+cn)},removeClass=function(el,cn){el.className=trim((" "+el.className+" ").replace(" "+cn+" "," "))},isArray=function(obj){return/Array/.test(Object.prototype.toString.call(obj))},isDate=function(obj){return/Date/.test(Object.prototype.toString.call(obj))&&!isNaN(obj.getTime())},isWeekend=function(date){var day=date.getDay();return 0===day||6===day},isLeapYear=function(year){return year%4===0&&year%100!==0||year%400===0},getDaysInMonth=function(year,month){return[31,isLeapYear(year)?29:28,31,30,31,30,31,31,30,31,30,31][month]},setToStartOfDay=function(date){isDate(date)&&date.setHours(0,0,0,0)},compareDates=function(a,b){return a.getTime()===b.getTime()},extend=function(to,from,overwrite){var prop,hasProp;for(prop in from)hasProp=void 0!==to[prop],hasProp&&"object"==typeof from[prop]&&null!==from[prop]&&void 0===from[prop].nodeName?isDate(from[prop])?overwrite&&(to[prop]=new Date(from[prop].getTime())):isArray(from[prop])?overwrite&&(to[prop]=from[prop].slice(0)):to[prop]=extend({},from[prop],overwrite):!overwrite&&hasProp||(to[prop]=from[prop]);return to},fireEvent=function(el,eventName,data){var ev;document.createEvent?(ev=document.createEvent("HTMLEvents"),ev.initEvent(eventName,!0,!1),ev=extend(ev,data),el.dispatchEvent(ev)):document.createEventObject&&(ev=document.createEventObject(),ev=extend(ev,data),el.fireEvent("on"+eventName,ev))},adjustCalendar=function(calendar){return calendar.month<0&&(calendar.year-=Math.ceil(Math.abs(calendar.month)/12),calendar.month+=12),calendar.month>11&&(calendar.year+=Math.floor(Math.abs(calendar.month)/12),calendar.month-=12),calendar},defaults={field:null,bound:void 0,position:"bottom left",reposition:!0,format:"YYYY-MM-DD",toString:null,parse:null,defaultDate:null,setDefaultDate:!1,firstDay:0,formatStrict:!1,minDate:null,maxDate:null,yearRange:10,showWeekNumber:!1,pickWholeWeek:!1,minYear:0,maxYear:9999,minMonth:void 0,maxMonth:void 0,startRange:null,endRange:null,isRTL:!1,yearSuffix:"",showMonthAfterYear:!1,showDaysInNextAndPreviousMonths:!1,enableSelectionDaysInNextAndPreviousMonths:!1,numberOfMonths:1,mainCalendar:"left",container:void 0,blurFieldOnSelect:!0,i18n:{previousMonth:"Previous Month",nextMonth:"Next Month",months:["January","February","March","April","May","June","July","August","September","October","November","December"],weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],weekdaysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},theme:null,events:[],onSelect:null,onOpen:null,onClose:null,onDraw:null},renderDayName=function(opts,day,abbr){for(day+=opts.firstDay;day>=7;)day-=7;return abbr?opts.i18n.weekdaysShort[day]:opts.i18n.weekdays[day]},renderDay=function(opts){var arr=[],ariaSelected="false";if(opts.isEmpty){if(!opts.showDaysInNextAndPreviousMonths)return'';arr.push("is-outside-current-month"),opts.enableSelectionDaysInNextAndPreviousMonths||arr.push("is-selection-disabled")}return opts.isDisabled&&arr.push("is-disabled"),opts.isToday&&arr.push("is-today"),opts.isSelected&&(arr.push("is-selected"),ariaSelected="true"),opts.hasEvent&&arr.push("has-event"),opts.isInRange&&arr.push("is-inrange"),opts.isStartRange&&arr.push("is-startrange"),opts.isEndRange&&arr.push("is-endrange"),'"},renderWeek=function(d,m,y){var onejan=new Date(y,0,1),weekNum=Math.ceil(((new Date(y,m,d)-onejan)/864e5+onejan.getDay()+1)/7);return''+weekNum+""},renderRow=function(days,isRTL,pickWholeWeek,isRowSelected){return''+(isRTL?days.reverse():days).join("")+""},renderBody=function(rows){return""+rows.join("")+""},renderHead=function(opts){var i,arr=[];for(opts.showWeekNumber&&arr.push(""),i=0;i<7;i++)arr.push(''+renderDayName(opts,i,!0)+"");return""+(opts.isRTL?arr.reverse():arr).join("")+""},renderTitle=function(instance,c,year,month,refYear,randId){var i,j,arr,monthHtml,yearHtml,opts=instance._o,isMinYear=year===opts.minYear,isMaxYear=year===opts.maxYear,html='
',prev=!0,next=!0;for(arr=[],i=0;i<12;i++)arr.push('");for(monthHtml='
'+opts.i18n.months[month]+'
",isArray(opts.yearRange)?(i=opts.yearRange[0],j=opts.yearRange[1]+1):(i=year-opts.yearRange,j=1+year+opts.yearRange),arr=[];i=opts.minYear&&arr.push('");return yearHtml='
'+year+opts.yearSuffix+'
",html+=opts.showMonthAfterYear?yearHtml+monthHtml:monthHtml+yearHtml,isMinYear&&(0===month||opts.minMonth>=month)&&(prev=!1),isMaxYear&&(11===month||opts.maxMonth<=month)&&(next=!1),0===c&&(html+='"),c===instance._o.numberOfMonths-1&&(html+='"),html+="
"},renderTable=function(opts,data,randId){return''+renderHead(opts)+renderBody(data)+"
"},Pikaday=function(options){var self=this,opts=self.config(options);self._onMouseDown=function(e){if(self._v){e=e||window.event;var target=e.target||e.srcElement;if(target)if(hasClass(target,"is-disabled")||(!hasClass(target,"pika-button")||hasClass(target,"is-empty")||hasClass(target.parentNode,"is-disabled")?hasClass(target,"pika-prev")?self.prevMonth():hasClass(target,"pika-next")&&self.nextMonth():(self.setDate(new Date(target.getAttribute("data-pika-year"),target.getAttribute("data-pika-month"),target.getAttribute("data-pika-day"))),opts.bound&&sto(function(){self.hide(),opts.blurFieldOnSelect&&opts.field&&opts.field.blur()},100))),hasClass(target,"pika-select"))self._c=!0;else{if(!e.preventDefault)return e.returnValue=!1,!1;e.preventDefault()}}},self._onChange=function(e){e=e||window.event;var target=e.target||e.srcElement;target&&(hasClass(target,"pika-select-month")?self.gotoMonth(target.value):hasClass(target,"pika-select-year")&&self.gotoYear(target.value))},self._onKeyChange=function(e){if(e=e||window.event,self.isVisible())switch(e.keyCode){case 13:case 27:opts.field&&opts.field.blur();break;case 37:e.preventDefault(),self.adjustDate("subtract",1);break;case 38:self.adjustDate("subtract",7);break;case 39:self.adjustDate("add",1);break;case 40:self.adjustDate("add",7)}},self._onInputChange=function(e){var date;e.firedBy!==self&&(opts.parse?date=opts.parse(opts.field.value,opts.format):hasMoment?(date=moment(opts.field.value,opts.format,opts.formatStrict),date=date&&date.isValid()?date.toDate():null):date=new Date(Date.parse(opts.field.value)),isDate(date)&&self.setDate(date),self._v||self.show())},self._onInputFocus=function(){self.show()},self._onInputClick=function(){self.show()},self._onInputBlur=function(){var pEl=document.activeElement;do if(hasClass(pEl,"pika-single"))return;while(pEl=pEl.parentNode);self._c||(self._b=sto(function(){self.hide()},50)),self._c=!1},self._onClick=function(e){e=e||window.event;var target=e.target||e.srcElement,pEl=target;if(target){!hasEventListeners&&hasClass(target,"pika-select")&&(target.onchange||(target.setAttribute("onchange","return;"),addEvent(target,"change",self._onChange)));do if(hasClass(pEl,"pika-single")||pEl===opts.trigger)return;while(pEl=pEl.parentNode);self._v&&target!==opts.trigger&&pEl!==opts.trigger&&self.hide()}},self.el=document.createElement("div"),self.el.className="pika-single"+(opts.isRTL?" is-rtl":"")+(opts.theme?" "+opts.theme:""),addEvent(self.el,"mousedown",self._onMouseDown,!0),addEvent(self.el,"touchend",self._onMouseDown,!0),addEvent(self.el,"change",self._onChange),addEvent(document,"keydown",self._onKeyChange),opts.field&&(opts.container?opts.container.appendChild(self.el):opts.bound?document.body.appendChild(self.el):opts.field.parentNode.insertBefore(self.el,opts.field.nextSibling),addEvent(opts.field,"change",self._onInputChange),opts.defaultDate||(hasMoment&&opts.field.value?opts.defaultDate=moment(opts.field.value,opts.format).toDate():opts.defaultDate=new Date(Date.parse(opts.field.value)),opts.setDefaultDate=!0));var defDate=opts.defaultDate;isDate(defDate)?opts.setDefaultDate?self.setDate(defDate,!0):self.gotoDate(defDate):self.gotoDate(new Date),opts.bound?(this.hide(),self.el.className+=" is-bound",addEvent(opts.trigger,"click",self._onInputClick),addEvent(opts.trigger,"focus",self._onInputFocus),addEvent(opts.trigger,"blur",self._onInputBlur)):this.show()};return Pikaday.prototype={config:function(options){this._o||(this._o=extend({},defaults,!0));var opts=extend(this._o,options,!0);opts.isRTL=!!opts.isRTL,opts.field=opts.field&&opts.field.nodeName?opts.field:null,opts.theme="string"==typeof opts.theme&&opts.theme?opts.theme:null,opts.bound=!!(void 0!==opts.bound?opts.field&&opts.bound:opts.field),opts.trigger=opts.trigger&&opts.trigger.nodeName?opts.trigger:opts.field,opts.disableWeekends=!!opts.disableWeekends,opts.disableDayFn="function"==typeof opts.disableDayFn?opts.disableDayFn:null;var nom=parseInt(opts.numberOfMonths,10)||1;if(opts.numberOfMonths=nom>4?4:nom,isDate(opts.minDate)||(opts.minDate=!1),isDate(opts.maxDate)||(opts.maxDate=!1),opts.minDate&&opts.maxDate&&opts.maxDate100&&(opts.yearRange=100);return opts},toString:function(format){return format=format||this._o.format,isDate(this._d)?this._o.toString?this._o.toString(this._d,format):hasMoment?moment(this._d).format(format):this._d.toDateString():""},getMoment:function(){return hasMoment?moment(this._d):null},setMoment:function(date,preventOnSelect){hasMoment&&moment.isMoment(date)&&this.setDate(date.toDate(),preventOnSelect)},getDate:function(){return isDate(this._d)?new Date(this._d.getTime()):null},setDate:function(date,preventOnSelect){if(!date)return this._d=null,this._o.field&&(this._o.field.value="",fireEvent(this._o.field,"change",{firedBy:this})),this.draw();if("string"==typeof date&&(date=new Date(Date.parse(date))),isDate(date)){var min=this._o.minDate,max=this._o.maxDate;isDate(min)&&datemax&&(date=max),this._d=new Date(date.getTime()),setToStartOfDay(this._d),this.gotoDate(this._d),this._o.field&&(this._o.field.value=this.toString(),fireEvent(this._o.field,"change",{firedBy:this})),preventOnSelect||"function"!=typeof this._o.onSelect||this._o.onSelect.call(this,this.getDate())}},gotoDate:function(date){var newCalendar=!0;if(isDate(date)){if(this.calendars){var firstVisibleDate=new Date(this.calendars[0].year,this.calendars[0].month,1),lastVisibleDate=new Date(this.calendars[this.calendars.length-1].year,this.calendars[this.calendars.length-1].month,1),visibleDate=date.getTime();lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1),lastVisibleDate.setDate(lastVisibleDate.getDate()-1),newCalendar=visibleDate=maxYear&&(this._y=maxYear,!isNaN(maxMonth)&&this._m>maxMonth&&(this._m=maxMonth)),randId="pika-title-"+Math.random().toString(36).replace(/[^a-z]+/g,"").substr(0,2);for(var c=0;c'+renderTitle(this,c,this.calendars[c].year,this.calendars[c].month,this.calendars[0].year,randId)+this.render(this.calendars[c].year,this.calendars[c].month,randId)+"";this.el.innerHTML=html,opts.bound&&"hidden"!==opts.field.type&&sto(function(){opts.trigger.focus()},1),"function"==typeof this._o.onDraw&&this._o.onDraw(this),opts.bound&&opts.field.setAttribute("aria-label","Use the arrow keys to pick a date")}},adjustPosition:function(){var field,pEl,width,height,viewportWidth,viewportHeight,scrollTop,left,top,clientRect;if(!this._o.container){if(this.el.style.position="absolute",field=this._o.trigger,pEl=field,width=this.el.offsetWidth,height=this.el.offsetHeight,viewportWidth=window.innerWidth||document.documentElement.clientWidth,viewportHeight=window.innerHeight||document.documentElement.clientHeight,scrollTop=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop,"function"==typeof field.getBoundingClientRect)clientRect=field.getBoundingClientRect(),left=clientRect.left+window.pageXOffset,top=clientRect.bottom+window.pageYOffset;else for(left=pEl.offsetLeft,top=pEl.offsetTop+pEl.offsetHeight;pEl=pEl.offsetParent;)left+=pEl.offsetLeft,top+=pEl.offsetTop;(this._o.reposition&&left+width>viewportWidth||this._o.position.indexOf("right")>-1&&left-width+field.offsetWidth>0)&&(left=left-width+field.offsetWidth),(this._o.reposition&&top+height>viewportHeight+scrollTop||this._o.position.indexOf("top")>-1&&top-height-field.offsetHeight>0)&&(top=top-height-field.offsetHeight),this.el.style.left=left+"px",this.el.style.top=top+"px"}},render:function(year,month,randId){var opts=this._o,now=new Date,days=getDaysInMonth(year,month),before=new Date(year,month,1).getDay(),data=[],row=[];setToStartOfDay(now),opts.firstDay>0&&(before-=opts.firstDay,before<0&&(before+=7));for(var previousMonth=0===month?11:month-1,nextMonth=11===month?0:month+1,yearOfPreviousMonth=0===month?year-1:year,yearOfNextMonth=11===month?year+1:year,daysInPreviousMonth=getDaysInMonth(yearOfPreviousMonth,previousMonth),cells=days+before,after=cells;after>7;)after-=7;cells+=7-after;for(var isWeekSelected=!1,i=0,r=0;i=days+before,dayNumber=1+(i-before),monthNumber=month,yearNumber=year,isStartRange=opts.startRange&&compareDates(opts.startRange,day),isEndRange=opts.endRange&&compareDates(opts.endRange,day),isInRange=opts.startRange&&opts.endRange&&opts.startRangeopts.maxDate||opts.disableWeekends&&isWeekend(day)||opts.disableDayFn&&opts.disableDayFn(day);isEmpty&&(i 11) { 169 | calendar.year += Math.floor(Math.abs(calendar.month)/12); 170 | calendar.month -= 12; 171 | } 172 | return calendar; 173 | }, 174 | 175 | /** 176 | * defaults and localisation 177 | */ 178 | defaults = { 179 | 180 | // bind the picker to a form field 181 | field: null, 182 | 183 | // automatically show/hide the picker on `field` focus (default `true` if `field` is set) 184 | bound: undefined, 185 | 186 | // position of the datepicker, relative to the field (default to bottom & left) 187 | // ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position) 188 | position: 'bottom left', 189 | 190 | // automatically fit in the viewport even if it means repositioning from the position option 191 | reposition: true, 192 | 193 | // the default output format for `.toString()` and `field` value 194 | format: 'YYYY-MM-DD', 195 | 196 | // the toString function which gets passed a current date object and format 197 | // and returns a string 198 | toString: null, 199 | 200 | // used to create date object from current input string 201 | parse: null, 202 | 203 | // the initial date to view when first opened 204 | defaultDate: null, 205 | 206 | // make the `defaultDate` the initial selected value 207 | setDefaultDate: false, 208 | 209 | // first day of week (0: Sunday, 1: Monday etc) 210 | firstDay: 0, 211 | 212 | // the default flag for moment's strict date parsing 213 | formatStrict: false, 214 | 215 | // the minimum/earliest date that can be selected 216 | minDate: null, 217 | // the maximum/latest date that can be selected 218 | maxDate: null, 219 | 220 | // number of years either side, or array of upper/lower range 221 | yearRange: 10, 222 | 223 | // show week numbers at head of row 224 | showWeekNumber: false, 225 | 226 | // Week picker mode 227 | pickWholeWeek: false, 228 | 229 | // used internally (don't config outside) 230 | minYear: 0, 231 | maxYear: 9999, 232 | minMonth: undefined, 233 | maxMonth: undefined, 234 | 235 | startRange: null, 236 | endRange: null, 237 | 238 | isRTL: false, 239 | 240 | // Additional text to append to the year in the calendar title 241 | yearSuffix: '', 242 | 243 | // Render the month after year in the calendar title 244 | showMonthAfterYear: false, 245 | 246 | // Render days of the calendar grid that fall in the next or previous month 247 | showDaysInNextAndPreviousMonths: false, 248 | 249 | // Allows user to select days that fall in the next or previous month 250 | enableSelectionDaysInNextAndPreviousMonths: false, 251 | 252 | // how many months are visible 253 | numberOfMonths: 1, 254 | 255 | // when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`) 256 | // only used for the first display or when a selected date is not visible 257 | mainCalendar: 'left', 258 | 259 | // Specify a DOM element to render the calendar in 260 | container: undefined, 261 | 262 | // Blur field when date is selected 263 | blurFieldOnSelect : true, 264 | 265 | // internationalization 266 | i18n: { 267 | previousMonth : 'Previous Month', 268 | nextMonth : 'Next Month', 269 | months : ['January','February','March','April','May','June','July','August','September','October','November','December'], 270 | weekdays : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], 271 | weekdaysShort : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] 272 | }, 273 | 274 | // Theme Classname 275 | theme: null, 276 | 277 | // events array 278 | events: [], 279 | 280 | // callback function 281 | onSelect: null, 282 | onOpen: null, 283 | onClose: null, 284 | onDraw: null 285 | }, 286 | 287 | 288 | /** 289 | * templating functions to abstract HTML rendering 290 | */ 291 | renderDayName = function(opts, day, abbr) 292 | { 293 | day += opts.firstDay; 294 | while (day >= 7) { 295 | day -= 7; 296 | } 297 | return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day]; 298 | }, 299 | 300 | renderDay = function(opts) 301 | { 302 | var arr = []; 303 | var ariaSelected = 'false'; 304 | if (opts.isEmpty) { 305 | if (opts.showDaysInNextAndPreviousMonths) { 306 | arr.push('is-outside-current-month'); 307 | 308 | if(!opts.enableSelectionDaysInNextAndPreviousMonths) { 309 | arr.push('is-selection-disabled'); 310 | } 311 | 312 | } else { 313 | return ''; 314 | } 315 | } 316 | if (opts.isDisabled) { 317 | arr.push('is-disabled'); 318 | } 319 | if (opts.isToday) { 320 | arr.push('is-today'); 321 | } 322 | if (opts.isSelected) { 323 | arr.push('is-selected'); 324 | ariaSelected = 'true'; 325 | } 326 | if (opts.hasEvent) { 327 | arr.push('has-event'); 328 | } 329 | if (opts.isInRange) { 330 | arr.push('is-inrange'); 331 | } 332 | if (opts.isStartRange) { 333 | arr.push('is-startrange'); 334 | } 335 | if (opts.isEndRange) { 336 | arr.push('is-endrange'); 337 | } 338 | return '' + 339 | '' + 343 | ''; 344 | }, 345 | 346 | renderWeek = function (d, m, y) { 347 | // Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified. 348 | var onejan = new Date(y, 0, 1), 349 | weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getDay()+1)/7); 350 | return '' + weekNum + ''; 351 | }, 352 | 353 | renderRow = function(days, isRTL, pickWholeWeek, isRowSelected) 354 | { 355 | return '' + (isRTL ? days.reverse() : days).join('') + ''; 356 | }, 357 | 358 | renderBody = function(rows) 359 | { 360 | return '' + rows.join('') + ''; 361 | }, 362 | 363 | renderHead = function(opts) 364 | { 365 | var i, arr = []; 366 | if (opts.showWeekNumber) { 367 | arr.push(''); 368 | } 369 | for (i = 0; i < 7; i++) { 370 | arr.push('' + renderDayName(opts, i, true) + ''); 371 | } 372 | return '' + (opts.isRTL ? arr.reverse() : arr).join('') + ''; 373 | }, 374 | 375 | renderTitle = function(instance, c, year, month, refYear, randId) 376 | { 377 | var i, j, arr, 378 | opts = instance._o, 379 | isMinYear = year === opts.minYear, 380 | isMaxYear = year === opts.maxYear, 381 | html = '
', 382 | monthHtml, 383 | yearHtml, 384 | prev = true, 385 | next = true; 386 | 387 | for (arr = [], i = 0; i < 12; i++) { 388 | arr.push(''); 392 | } 393 | 394 | monthHtml = '
' + opts.i18n.months[month] + '
'; 395 | 396 | if (isArray(opts.yearRange)) { 397 | i = opts.yearRange[0]; 398 | j = opts.yearRange[1] + 1; 399 | } else { 400 | i = year - opts.yearRange; 401 | j = 1 + year + opts.yearRange; 402 | } 403 | 404 | for (arr = []; i < j && i <= opts.maxYear; i++) { 405 | if (i >= opts.minYear) { 406 | arr.push(''); 407 | } 408 | } 409 | yearHtml = '
' + year + opts.yearSuffix + '
'; 410 | 411 | if (opts.showMonthAfterYear) { 412 | html += yearHtml + monthHtml; 413 | } else { 414 | html += monthHtml + yearHtml; 415 | } 416 | 417 | if (isMinYear && (month === 0 || opts.minMonth >= month)) { 418 | prev = false; 419 | } 420 | 421 | if (isMaxYear && (month === 11 || opts.maxMonth <= month)) { 422 | next = false; 423 | } 424 | 425 | if (c === 0) { 426 | html += ''; 427 | } 428 | if (c === (instance._o.numberOfMonths - 1) ) { 429 | html += ''; 430 | } 431 | 432 | return html += '
'; 433 | }, 434 | 435 | renderTable = function(opts, data, randId) 436 | { 437 | return '' + renderHead(opts) + renderBody(data) + '
'; 438 | }, 439 | 440 | 441 | /** 442 | * Pikaday constructor 443 | */ 444 | Pikaday = function(options) 445 | { 446 | var self = this, 447 | opts = self.config(options); 448 | 449 | self._onMouseDown = function(e) 450 | { 451 | if (!self._v) { 452 | return; 453 | } 454 | e = e || window.event; 455 | var target = e.target || e.srcElement; 456 | if (!target) { 457 | return; 458 | } 459 | 460 | if (!hasClass(target, 'is-disabled')) { 461 | if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty') && !hasClass(target.parentNode, 'is-disabled')) { 462 | self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day'))); 463 | if (opts.bound) { 464 | sto(function() { 465 | self.hide(); 466 | if (opts.blurFieldOnSelect && opts.field) { 467 | opts.field.blur(); 468 | } 469 | }, 100); 470 | } 471 | } 472 | else if (hasClass(target, 'pika-prev')) { 473 | self.prevMonth(); 474 | } 475 | else if (hasClass(target, 'pika-next')) { 476 | self.nextMonth(); 477 | } 478 | } 479 | if (!hasClass(target, 'pika-select')) { 480 | // if this is touch event prevent mouse events emulation 481 | if (e.preventDefault) { 482 | e.preventDefault(); 483 | } else { 484 | e.returnValue = false; 485 | return false; 486 | } 487 | } else { 488 | self._c = true; 489 | } 490 | }; 491 | 492 | self._onChange = function(e) 493 | { 494 | e = e || window.event; 495 | var target = e.target || e.srcElement; 496 | if (!target) { 497 | return; 498 | } 499 | if (hasClass(target, 'pika-select-month')) { 500 | self.gotoMonth(target.value); 501 | } 502 | else if (hasClass(target, 'pika-select-year')) { 503 | self.gotoYear(target.value); 504 | } 505 | }; 506 | 507 | self._onKeyChange = function(e) 508 | { 509 | e = e || window.event; 510 | 511 | if (self.isVisible()) { 512 | 513 | switch(e.keyCode){ 514 | case 13: 515 | case 27: 516 | if (opts.field) { 517 | opts.field.blur(); 518 | } 519 | break; 520 | case 37: 521 | e.preventDefault(); 522 | self.adjustDate('subtract', 1); 523 | break; 524 | case 38: 525 | self.adjustDate('subtract', 7); 526 | break; 527 | case 39: 528 | self.adjustDate('add', 1); 529 | break; 530 | case 40: 531 | self.adjustDate('add', 7); 532 | break; 533 | } 534 | } 535 | }; 536 | 537 | self._onInputChange = function(e) 538 | { 539 | var date; 540 | 541 | if (e.firedBy === self) { 542 | return; 543 | } 544 | if (opts.parse) { 545 | date = opts.parse(opts.field.value, opts.format); 546 | } else if (hasMoment) { 547 | date = moment(opts.field.value, opts.format, opts.formatStrict); 548 | date = (date && date.isValid()) ? date.toDate() : null; 549 | } 550 | else { 551 | date = new Date(Date.parse(opts.field.value)); 552 | } 553 | if (isDate(date)) { 554 | self.setDate(date); 555 | } 556 | if (!self._v) { 557 | self.show(); 558 | } 559 | }; 560 | 561 | self._onInputFocus = function() 562 | { 563 | self.show(); 564 | }; 565 | 566 | self._onInputClick = function() 567 | { 568 | self.show(); 569 | }; 570 | 571 | self._onInputBlur = function() 572 | { 573 | // IE allows pika div to gain focus; catch blur the input field 574 | var pEl = document.activeElement; 575 | do { 576 | if (hasClass(pEl, 'pika-single')) { 577 | return; 578 | } 579 | } 580 | while ((pEl = pEl.parentNode)); 581 | 582 | if (!self._c) { 583 | self._b = sto(function() { 584 | self.hide(); 585 | }, 50); 586 | } 587 | self._c = false; 588 | }; 589 | 590 | self._onClick = function(e) 591 | { 592 | e = e || window.event; 593 | var target = e.target || e.srcElement, 594 | pEl = target; 595 | if (!target) { 596 | return; 597 | } 598 | if (!hasEventListeners && hasClass(target, 'pika-select')) { 599 | if (!target.onchange) { 600 | target.setAttribute('onchange', 'return;'); 601 | addEvent(target, 'change', self._onChange); 602 | } 603 | } 604 | do { 605 | if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) { 606 | return; 607 | } 608 | } 609 | while ((pEl = pEl.parentNode)); 610 | if (self._v && target !== opts.trigger && pEl !== opts.trigger) { 611 | self.hide(); 612 | } 613 | }; 614 | 615 | self.el = document.createElement('div'); 616 | self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : ''); 617 | 618 | addEvent(self.el, 'mousedown', self._onMouseDown, true); 619 | addEvent(self.el, 'touchend', self._onMouseDown, true); 620 | addEvent(self.el, 'change', self._onChange); 621 | addEvent(document, 'keydown', self._onKeyChange); 622 | 623 | if (opts.field) { 624 | if (opts.container) { 625 | opts.container.appendChild(self.el); 626 | } else if (opts.bound) { 627 | document.body.appendChild(self.el); 628 | } else { 629 | opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling); 630 | } 631 | addEvent(opts.field, 'change', self._onInputChange); 632 | 633 | if (!opts.defaultDate) { 634 | if (hasMoment && opts.field.value) { 635 | opts.defaultDate = moment(opts.field.value, opts.format).toDate(); 636 | } else { 637 | opts.defaultDate = new Date(Date.parse(opts.field.value)); 638 | } 639 | opts.setDefaultDate = true; 640 | } 641 | } 642 | 643 | var defDate = opts.defaultDate; 644 | 645 | if (isDate(defDate)) { 646 | if (opts.setDefaultDate) { 647 | self.setDate(defDate, true); 648 | } else { 649 | self.gotoDate(defDate); 650 | } 651 | } else { 652 | self.gotoDate(new Date()); 653 | } 654 | 655 | if (opts.bound) { 656 | this.hide(); 657 | self.el.className += ' is-bound'; 658 | addEvent(opts.trigger, 'click', self._onInputClick); 659 | addEvent(opts.trigger, 'focus', self._onInputFocus); 660 | addEvent(opts.trigger, 'blur', self._onInputBlur); 661 | } else { 662 | this.show(); 663 | } 664 | }; 665 | 666 | 667 | /** 668 | * public Pikaday API 669 | */ 670 | Pikaday.prototype = { 671 | 672 | 673 | /** 674 | * configure functionality 675 | */ 676 | config: function(options) 677 | { 678 | if (!this._o) { 679 | this._o = extend({}, defaults, true); 680 | } 681 | 682 | var opts = extend(this._o, options, true); 683 | 684 | opts.isRTL = !!opts.isRTL; 685 | 686 | opts.field = (opts.field && opts.field.nodeName) ? opts.field : null; 687 | 688 | opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null; 689 | 690 | opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field); 691 | 692 | opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field; 693 | 694 | opts.disableWeekends = !!opts.disableWeekends; 695 | 696 | opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null; 697 | 698 | var nom = parseInt(opts.numberOfMonths, 10) || 1; 699 | opts.numberOfMonths = nom > 4 ? 4 : nom; 700 | 701 | if (!isDate(opts.minDate)) { 702 | opts.minDate = false; 703 | } 704 | if (!isDate(opts.maxDate)) { 705 | opts.maxDate = false; 706 | } 707 | if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) { 708 | opts.maxDate = opts.minDate = false; 709 | } 710 | if (opts.minDate) { 711 | this.setMinDate(opts.minDate); 712 | } 713 | if (opts.maxDate) { 714 | this.setMaxDate(opts.maxDate); 715 | } 716 | 717 | if (isArray(opts.yearRange)) { 718 | var fallback = new Date().getFullYear() - 10; 719 | opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback; 720 | opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback; 721 | } else { 722 | opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange; 723 | if (opts.yearRange > 100) { 724 | opts.yearRange = 100; 725 | } 726 | } 727 | 728 | return opts; 729 | }, 730 | 731 | /** 732 | * return a formatted string of the current selection (using Moment.js if available) 733 | */ 734 | toString: function(format) 735 | { 736 | format = format || this._o.format; 737 | if (!isDate(this._d)) { 738 | return ''; 739 | } 740 | if (this._o.toString) { 741 | return this._o.toString(this._d, format); 742 | } 743 | if (hasMoment) { 744 | return moment(this._d).format(format); 745 | } 746 | return this._d.toDateString(); 747 | }, 748 | 749 | /** 750 | * return a Moment.js object of the current selection (if available) 751 | */ 752 | getMoment: function() 753 | { 754 | return hasMoment ? moment(this._d) : null; 755 | }, 756 | 757 | /** 758 | * set the current selection from a Moment.js object (if available) 759 | */ 760 | setMoment: function(date, preventOnSelect) 761 | { 762 | if (hasMoment && moment.isMoment(date)) { 763 | this.setDate(date.toDate(), preventOnSelect); 764 | } 765 | }, 766 | 767 | /** 768 | * return a Date object of the current selection 769 | */ 770 | getDate: function() 771 | { 772 | return isDate(this._d) ? new Date(this._d.getTime()) : null; 773 | }, 774 | 775 | /** 776 | * set the current selection 777 | */ 778 | setDate: function(date, preventOnSelect) 779 | { 780 | if (!date) { 781 | this._d = null; 782 | 783 | if (this._o.field) { 784 | this._o.field.value = ''; 785 | fireEvent(this._o.field, 'change', { firedBy: this }); 786 | } 787 | 788 | return this.draw(); 789 | } 790 | if (typeof date === 'string') { 791 | date = new Date(Date.parse(date)); 792 | } 793 | if (!isDate(date)) { 794 | return; 795 | } 796 | 797 | var min = this._o.minDate, 798 | max = this._o.maxDate; 799 | 800 | if (isDate(min) && date < min) { 801 | date = min; 802 | } else if (isDate(max) && date > max) { 803 | date = max; 804 | } 805 | 806 | this._d = new Date(date.getTime()); 807 | setToStartOfDay(this._d); 808 | this.gotoDate(this._d); 809 | 810 | if (this._o.field) { 811 | this._o.field.value = this.toString(); 812 | fireEvent(this._o.field, 'change', { firedBy: this }); 813 | } 814 | if (!preventOnSelect && typeof this._o.onSelect === 'function') { 815 | this._o.onSelect.call(this, this.getDate()); 816 | } 817 | }, 818 | 819 | /** 820 | * change view to a specific date 821 | */ 822 | gotoDate: function(date) 823 | { 824 | var newCalendar = true; 825 | 826 | if (!isDate(date)) { 827 | return; 828 | } 829 | 830 | if (this.calendars) { 831 | var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1), 832 | lastVisibleDate = new Date(this.calendars[this.calendars.length-1].year, this.calendars[this.calendars.length-1].month, 1), 833 | visibleDate = date.getTime(); 834 | // get the end of the month 835 | lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1); 836 | lastVisibleDate.setDate(lastVisibleDate.getDate()-1); 837 | newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate); 838 | } 839 | 840 | if (newCalendar) { 841 | this.calendars = [{ 842 | month: date.getMonth(), 843 | year: date.getFullYear() 844 | }]; 845 | if (this._o.mainCalendar === 'right') { 846 | this.calendars[0].month += 1 - this._o.numberOfMonths; 847 | } 848 | } 849 | 850 | this.adjustCalendars(); 851 | }, 852 | 853 | adjustDate: function(sign, days) { 854 | 855 | var day = this.getDate() || new Date(); 856 | var difference = parseInt(days)*24*60*60*1000; 857 | 858 | var newDay; 859 | 860 | if (sign === 'add') { 861 | newDay = new Date(day.valueOf() + difference); 862 | } else if (sign === 'subtract') { 863 | newDay = new Date(day.valueOf() - difference); 864 | } 865 | 866 | this.setDate(newDay); 867 | }, 868 | 869 | adjustCalendars: function() { 870 | this.calendars[0] = adjustCalendar(this.calendars[0]); 871 | for (var c = 1; c < this._o.numberOfMonths; c++) { 872 | this.calendars[c] = adjustCalendar({ 873 | month: this.calendars[0].month + c, 874 | year: this.calendars[0].year 875 | }); 876 | } 877 | this.draw(); 878 | }, 879 | 880 | gotoToday: function() 881 | { 882 | this.gotoDate(new Date()); 883 | }, 884 | 885 | /** 886 | * change view to a specific month (zero-index, e.g. 0: January) 887 | */ 888 | gotoMonth: function(month) 889 | { 890 | if (!isNaN(month)) { 891 | this.calendars[0].month = parseInt(month, 10); 892 | this.adjustCalendars(); 893 | } 894 | }, 895 | 896 | nextMonth: function() 897 | { 898 | this.calendars[0].month++; 899 | this.adjustCalendars(); 900 | }, 901 | 902 | prevMonth: function() 903 | { 904 | this.calendars[0].month--; 905 | this.adjustCalendars(); 906 | }, 907 | 908 | /** 909 | * change view to a specific full year (e.g. "2012") 910 | */ 911 | gotoYear: function(year) 912 | { 913 | if (!isNaN(year)) { 914 | this.calendars[0].year = parseInt(year, 10); 915 | this.adjustCalendars(); 916 | } 917 | }, 918 | 919 | /** 920 | * change the minDate 921 | */ 922 | setMinDate: function(value) 923 | { 924 | if(value instanceof Date) { 925 | setToStartOfDay(value); 926 | this._o.minDate = value; 927 | this._o.minYear = value.getFullYear(); 928 | this._o.minMonth = value.getMonth(); 929 | } else { 930 | this._o.minDate = defaults.minDate; 931 | this._o.minYear = defaults.minYear; 932 | this._o.minMonth = defaults.minMonth; 933 | this._o.startRange = defaults.startRange; 934 | } 935 | 936 | this.draw(); 937 | }, 938 | 939 | /** 940 | * change the maxDate 941 | */ 942 | setMaxDate: function(value) 943 | { 944 | if(value instanceof Date) { 945 | setToStartOfDay(value); 946 | this._o.maxDate = value; 947 | this._o.maxYear = value.getFullYear(); 948 | this._o.maxMonth = value.getMonth(); 949 | } else { 950 | this._o.maxDate = defaults.maxDate; 951 | this._o.maxYear = defaults.maxYear; 952 | this._o.maxMonth = defaults.maxMonth; 953 | this._o.endRange = defaults.endRange; 954 | } 955 | 956 | this.draw(); 957 | }, 958 | 959 | setStartRange: function(value) 960 | { 961 | this._o.startRange = value; 962 | }, 963 | 964 | setEndRange: function(value) 965 | { 966 | this._o.endRange = value; 967 | }, 968 | 969 | /** 970 | * refresh the HTML 971 | */ 972 | draw: function(force) 973 | { 974 | if (!this._v && !force) { 975 | return; 976 | } 977 | var opts = this._o, 978 | minYear = opts.minYear, 979 | maxYear = opts.maxYear, 980 | minMonth = opts.minMonth, 981 | maxMonth = opts.maxMonth, 982 | html = '', 983 | randId; 984 | 985 | if (this._y <= minYear) { 986 | this._y = minYear; 987 | if (!isNaN(minMonth) && this._m < minMonth) { 988 | this._m = minMonth; 989 | } 990 | } 991 | if (this._y >= maxYear) { 992 | this._y = maxYear; 993 | if (!isNaN(maxMonth) && this._m > maxMonth) { 994 | this._m = maxMonth; 995 | } 996 | } 997 | 998 | randId = 'pika-title-' + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2); 999 | 1000 | for (var c = 0; c < opts.numberOfMonths; c++) { 1001 | html += '
' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year, randId) + this.render(this.calendars[c].year, this.calendars[c].month, randId) + '
'; 1002 | } 1003 | 1004 | this.el.innerHTML = html; 1005 | 1006 | if (opts.bound) { 1007 | if(opts.field.type !== 'hidden') { 1008 | sto(function() { 1009 | opts.trigger.focus(); 1010 | }, 1); 1011 | } 1012 | } 1013 | 1014 | if (typeof this._o.onDraw === 'function') { 1015 | this._o.onDraw(this); 1016 | } 1017 | 1018 | if (opts.bound) { 1019 | // let the screen reader user know to use arrow keys 1020 | opts.field.setAttribute('aria-label', 'Use the arrow keys to pick a date'); 1021 | } 1022 | }, 1023 | 1024 | adjustPosition: function() 1025 | { 1026 | var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect; 1027 | 1028 | if (this._o.container) return; 1029 | 1030 | this.el.style.position = 'absolute'; 1031 | 1032 | field = this._o.trigger; 1033 | pEl = field; 1034 | width = this.el.offsetWidth; 1035 | height = this.el.offsetHeight; 1036 | viewportWidth = window.innerWidth || document.documentElement.clientWidth; 1037 | viewportHeight = window.innerHeight || document.documentElement.clientHeight; 1038 | scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; 1039 | 1040 | if (typeof field.getBoundingClientRect === 'function') { 1041 | clientRect = field.getBoundingClientRect(); 1042 | left = clientRect.left + window.pageXOffset; 1043 | top = clientRect.bottom + window.pageYOffset; 1044 | } else { 1045 | left = pEl.offsetLeft; 1046 | top = pEl.offsetTop + pEl.offsetHeight; 1047 | while((pEl = pEl.offsetParent)) { 1048 | left += pEl.offsetLeft; 1049 | top += pEl.offsetTop; 1050 | } 1051 | } 1052 | 1053 | // default position is bottom & left 1054 | if ((this._o.reposition && left + width > viewportWidth) || 1055 | ( 1056 | this._o.position.indexOf('right') > -1 && 1057 | left - width + field.offsetWidth > 0 1058 | ) 1059 | ) { 1060 | left = left - width + field.offsetWidth; 1061 | } 1062 | if ((this._o.reposition && top + height > viewportHeight + scrollTop) || 1063 | ( 1064 | this._o.position.indexOf('top') > -1 && 1065 | top - height - field.offsetHeight > 0 1066 | ) 1067 | ) { 1068 | top = top - height - field.offsetHeight; 1069 | } 1070 | 1071 | this.el.style.left = left + 'px'; 1072 | this.el.style.top = top + 'px'; 1073 | }, 1074 | 1075 | /** 1076 | * render HTML for a particular month 1077 | */ 1078 | render: function(year, month, randId) 1079 | { 1080 | var opts = this._o, 1081 | now = new Date(), 1082 | days = getDaysInMonth(year, month), 1083 | before = new Date(year, month, 1).getDay(), 1084 | data = [], 1085 | row = []; 1086 | setToStartOfDay(now); 1087 | if (opts.firstDay > 0) { 1088 | before -= opts.firstDay; 1089 | if (before < 0) { 1090 | before += 7; 1091 | } 1092 | } 1093 | var previousMonth = month === 0 ? 11 : month - 1, 1094 | nextMonth = month === 11 ? 0 : month + 1, 1095 | yearOfPreviousMonth = month === 0 ? year - 1 : year, 1096 | yearOfNextMonth = month === 11 ? year + 1 : year, 1097 | daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth); 1098 | var cells = days + before, 1099 | after = cells; 1100 | while(after > 7) { 1101 | after -= 7; 1102 | } 1103 | cells += 7 - after; 1104 | var isWeekSelected = false; 1105 | for (var i = 0, r = 0; i < cells; i++) 1106 | { 1107 | var day = new Date(year, month, 1 + (i - before)), 1108 | isSelected = isDate(this._d) ? compareDates(day, this._d) : false, 1109 | isToday = compareDates(day, now), 1110 | hasEvent = opts.events.indexOf(day.toDateString()) !== -1 ? true : false, 1111 | isEmpty = i < before || i >= (days + before), 1112 | dayNumber = 1 + (i - before), 1113 | monthNumber = month, 1114 | yearNumber = year, 1115 | isStartRange = opts.startRange && compareDates(opts.startRange, day), 1116 | isEndRange = opts.endRange && compareDates(opts.endRange, day), 1117 | isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange, 1118 | isDisabled = (opts.minDate && day < opts.minDate) || 1119 | (opts.maxDate && day > opts.maxDate) || 1120 | (opts.disableWeekends && isWeekend(day)) || 1121 | (opts.disableDayFn && opts.disableDayFn(day)); 1122 | 1123 | if (isEmpty) { 1124 | if (i < before) { 1125 | dayNumber = daysInPreviousMonth + dayNumber; 1126 | monthNumber = previousMonth; 1127 | yearNumber = yearOfPreviousMonth; 1128 | } else { 1129 | dayNumber = dayNumber - days; 1130 | monthNumber = nextMonth; 1131 | yearNumber = yearOfNextMonth; 1132 | } 1133 | } 1134 | 1135 | var dayConfig = { 1136 | day: dayNumber, 1137 | month: monthNumber, 1138 | year: yearNumber, 1139 | hasEvent: hasEvent, 1140 | isSelected: isSelected, 1141 | isToday: isToday, 1142 | isDisabled: isDisabled, 1143 | isEmpty: isEmpty, 1144 | isStartRange: isStartRange, 1145 | isEndRange: isEndRange, 1146 | isInRange: isInRange, 1147 | showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths, 1148 | enableSelectionDaysInNextAndPreviousMonths: opts.enableSelectionDaysInNextAndPreviousMonths 1149 | }; 1150 | 1151 | if (opts.pickWholeWeek && isSelected) { 1152 | isWeekSelected = true; 1153 | } 1154 | 1155 | row.push(renderDay(dayConfig)); 1156 | 1157 | if (++r === 7) { 1158 | if (opts.showWeekNumber) { 1159 | row.unshift(renderWeek(i - before, month, year)); 1160 | } 1161 | data.push(renderRow(row, opts.isRTL, opts.pickWholeWeek, isWeekSelected)); 1162 | row = []; 1163 | r = 0; 1164 | isWeekSelected = false; 1165 | } 1166 | } 1167 | return renderTable(opts, data, randId); 1168 | }, 1169 | 1170 | isVisible: function() 1171 | { 1172 | return this._v; 1173 | }, 1174 | 1175 | show: function() 1176 | { 1177 | if (!this.isVisible()) { 1178 | this._v = true; 1179 | this.draw(); 1180 | removeClass(this.el, 'is-hidden'); 1181 | if (this._o.bound) { 1182 | addEvent(document, 'click', this._onClick); 1183 | this.adjustPosition(); 1184 | } 1185 | if (typeof this._o.onOpen === 'function') { 1186 | this._o.onOpen.call(this); 1187 | } 1188 | } 1189 | }, 1190 | 1191 | hide: function() 1192 | { 1193 | var v = this._v; 1194 | if (v !== false) { 1195 | if (this._o.bound) { 1196 | removeEvent(document, 'click', this._onClick); 1197 | } 1198 | this.el.style.position = 'static'; // reset 1199 | this.el.style.left = 'auto'; 1200 | this.el.style.top = 'auto'; 1201 | addClass(this.el, 'is-hidden'); 1202 | this._v = false; 1203 | if (v !== undefined && typeof this._o.onClose === 'function') { 1204 | this._o.onClose.call(this); 1205 | } 1206 | } 1207 | }, 1208 | 1209 | /** 1210 | * GAME OVER 1211 | */ 1212 | destroy: function() 1213 | { 1214 | this.hide(); 1215 | removeEvent(this.el, 'mousedown', this._onMouseDown, true); 1216 | removeEvent(this.el, 'touchend', this._onMouseDown, true); 1217 | removeEvent(this.el, 'change', this._onChange); 1218 | removeEvent(document, 'keydown', this._onKeyChange); 1219 | if (this._o.field) { 1220 | removeEvent(this._o.field, 'change', this._onInputChange); 1221 | if (this._o.bound) { 1222 | removeEvent(this._o.trigger, 'click', this._onInputClick); 1223 | removeEvent(this._o.trigger, 'focus', this._onInputFocus); 1224 | removeEvent(this._o.trigger, 'blur', this._onInputBlur); 1225 | } 1226 | } 1227 | if (this.el.parentNode) { 1228 | this.el.parentNode.removeChild(this.el); 1229 | } 1230 | } 1231 | 1232 | }; 1233 | 1234 | return Pikaday; 1235 | 1236 | })); 1237 | -------------------------------------------------------------------------------- /dist/dependencies/moment.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function c(){return e.apply(null,arguments)}function o(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function u(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e){return void 0===e}function d(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function h(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,s=[];for(n=0;n>>0,s=0;sDe(e)?(r=e+1,a=o-De(e)):(r=e,a=o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(De(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),H("week","w"),H("isoWeek","W"),L("week",5),L("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=k(e)});I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),H("day","d"),H("weekday","e"),H("isoWeekday","E"),L("day",11),L("weekday",11),L("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=k(e)});var je="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var ze="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var $e=ae;var qe=ae;var Je=ae;function Be(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=de(o[t]),u[t]=de(u[t]),l[t]=de(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Qe(){return this.hours()%12||12}function Xe(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Ke(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Qe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Qe.apply(this)+U(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Qe.apply(this)+U(this.minutes(),2)+U(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+U(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+U(this.minutes(),2)+U(this.seconds(),2)}),Xe("a",!0),Xe("A",!1),H("hour","h"),L("hour",13),ue("a",Ke),ue("A",Ke),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=k(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=k(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s,2)),t[ve]=k(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s,2)),t[ve]=k(e.substr(i))});var et,tt=Te("Hours",!0),nt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:He,monthsShort:Re,week:{dow:0,doy:6},weekdays:je,weekdaysMin:ze,weekdaysShort:Ze,meridiemParse:/[ap]\.?m?\.?/i},st={},it={};function rt(e){return e?e.toLowerCase().replace("_","-"):e}function at(e){var t=null;if(!st[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=et._abbr,require("./locale/"+e),ot(t)}catch(e){}return st[e]}function ot(e,t){var n;return e&&((n=l(t)?lt(e):ut(e,t))?et=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),et._abbr}function ut(e,t){if(null!==t){var n,s=nt;if(t.abbr=e,null!=st[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=st[e]._config;else if(null!=t.parentLocale)if(null!=st[t.parentLocale])s=st[t.parentLocale]._config;else{if(null==(n=at(t.parentLocale)))return it[t.parentLocale]||(it[t.parentLocale]=[]),it[t.parentLocale].push({name:e,config:t}),null;s=n._config}return st[e]=new P(b(s,t)),it[e]&&it[e].forEach(function(e){ut(e.name,e.config)}),ot(e),st[e]}return delete st[e],null}function lt(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return et;if(!o(e)){if(t=at(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r=t&&a(i,n,!0)>=t-1)break;t--}r++}return et}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11Pe(n[me],n[_e])?ye:n[ge]<0||24Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ht(e._a[me],s[me]),(e._dayOfYear>De(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[pe]&&0===e._a[ve]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o=new Date(e,t,n,s,i,r,a);return e<100&&0<=e&&isFinite(o.getFullYear())&&o.setFullYear(e),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/Z|[+-]\d\d(?::?\d\d)?/,yt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],gt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function vt(e){var t,n,s,i,r,a,o=e._i,u=ft.exec(o)||mt.exec(o);if(u){for(g(e).iso=!0,t=0,n=yt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},ln.isLocal=function(){return!!this.isValid()&&!this._isUTC},ln.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},ln.isUtc=Vt,ln.isUTC=Vt,ln.zoneAbbr=function(){return this._isUTC?"UTC":""},ln.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},ln.dates=n("dates accessor is deprecated. Use date instead.",nn),ln.months=n("months accessor is deprecated. Use month instead",Fe),ln.years=n("years accessor is deprecated. Use year instead",Oe),ln.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),ln.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Yt(e))._a){var t=e._isUTC?y(e._a):Tt(e._a);this._isDSTShifted=this.isValid()&&0