[Pkg-javascript-commits] [eonasdan-bootstrap-datetimepicker] 01/02: Imported Upstream version 4.17.37
Mathias Behrle
mbehrle at moszumanska.debian.org
Sat Jul 23 13:26:16 UTC 2016
This is an automated email from the git hooks/post-receive script.
mbehrle pushed a commit to branch master
in repository eonasdan-bootstrap-datetimepicker.
commit 83756271a7e0850305ee20c07520028e9a7a9e87
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Wed Jul 13 11:06:24 2016 +0200
Imported Upstream version 4.17.37
---
.gitattributes | 63 +
.gitignore | 12 +
.jscs.json | 62 +
.npmignore | 16 +
.travis.yml | 9 +
CONTRIBUTING.md | 38 +
Gruntfile.js | 191 ++
LICENSE | 21 +
README.md | 37 +
bower.json | 35 +
build/css/bootstrap-datetimepicker-standalone.css | 103 +
build/css/bootstrap-datetimepicker.css | 373 +++
build/css/bootstrap-datetimepicker.min.css | 5 +
build/js/bootstrap-datetimepicker.min.js | 9 +
component.json | 9 +
composer.json | 28 +
docs/ChangeLog.md | 222 ++
docs/ContributorsGuide.md | 126 +
docs/Events.md | 96 +
docs/Extras.md | 95 +
docs/FAQ.md | 22 +
docs/Functions.md | 670 ++++++
docs/Installing.md | 161 ++
docs/Options.md | 473 ++++
docs/Version 4 Changelog.md | 6 +
docs/Version 4 Contributors guide.md | 6 +
docs/index.md | 592 +++++
mkdocs.yml | 17 +
package.json | 43 +
src/js/bootstrap-datetimepicker.js | 2552 +++++++++++++++++++++
src/less/_bootstrap-datetimepicker.less | 352 +++
src/less/bootstrap-datetimepicker-build.less | 17 +
src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec | 34 +
src/nuget/Bootstrap.v3.Datetimepicker.nuspec | 34 +
src/nuget/NuGet.exe | Bin 0 -> 1662976 bytes
src/nuget/install.ps1 | 2 +
src/sass/_bootstrap-datetimepicker.scss | 343 +++
src/sass/bootstrap-datetimepicker-build.scss | 16 +
tasks/bump_version.js | 88 +
test/publicApiSpec.js | 708 ++++++
test/screen-capture/base.html | 65 +
test/screen-capture/compile.js | 17 +
test/screen-capture/index.js | 24 +
test/screen-capture/out/.gitignore | 1 +
test/screen-capture/pic/.gitignore | 1 +
test/screen-capture/t1.html | 11 +
test/screen-capture/t2.html | 7 +
test/screen-capture/t3.html | 6 +
test/screen-capture/t4.html | 6 +
test/screen-capture/t5.html | 1 +
50 files changed, 7825 insertions(+)
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f118199
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+bower_components
+node_modules
+.idea
+.vs
+obj
+bin
+site/
+*.user
+*.csproj
+*.sln
+*.nupkg
+index.html
\ No newline at end of file
diff --git a/.jscs.json b/.jscs.json
new file mode 100644
index 0000000..8af2b61
--- /dev/null
+++ b/.jscs.json
@@ -0,0 +1,62 @@
+{
+ "requireCurlyBraces": [
+ "if",
+ "else",
+ "for",
+ "while",
+ "do",
+ "try",
+ "catch"
+ ],
+ "requireSpaceAfterKeywords": [
+ "if",
+ "else",
+ "for",
+ "while",
+ "do",
+ "switch",
+ "return",
+ "try",
+ "catch"
+ ],
+ "requireSpaceBeforeBlockStatements": true,
+ "requireParenthesesAroundIIFE": true,
+ "requireSpacesInConditionalExpression": true,
+ "requireSpacesInAnonymousFunctionExpression": {
+ "beforeOpeningRoundBrace": true,
+ "beforeOpeningCurlyBrace": true
+ },
+ "requireSpacesInNamedFunctionExpression": {
+ "beforeOpeningCurlyBrace": true
+ },
+ "disallowSpacesInNamedFunctionExpression": {
+ "beforeOpeningRoundBrace": true
+ },
+ "requireMultipleVarDecl": "onevar",
+ "requireBlocksOnNewline": true,
+ "disallowPaddingNewlinesInBlocks": true,
+ "disallowEmptyBlocks": true,
+ "disallowSpacesInsideObjectBrackets": true,
+ "disallowSpacesInsideArrayBrackets": true,
+ "disallowSpacesInsideParentheses": true,
+ "requireCommaBeforeLineBreak": true,
+ "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
+ "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
+ "requireSpaceBeforeBinaryOperators": [
+ "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
+ "&=", "|=", "^=",
+
+ "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
+ "|", "^", "&&", "||", "===", "==", ">=",
+ "<=", "<", ">", "!=", "!=="
+ ],
+ "requireSpaceAfterBinaryOperators": true,
+ "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
+ "disallowKeywords": ["with"],
+ "disallowMultipleLineStrings": true,
+ "validateIndentation": 4,
+ "disallowTrailingWhitespace": true,
+ "disallowTrailingComma": true,
+ "requireLineFeedAtFileEnd": true,
+ "requireCapitalizedConstructors": true
+}
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..7d59916
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,16 @@
+/assets
+/_includes
+/_layouts
+/.gitignore
+/node_modules
+/Makefile
+/test
+*.log
+*.swp
+*~
+*.tgz
+/site
+*.user
+*.csproj
+*.sln
+*.nupkg
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a716cb3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: node_js
+sudo: false
+node_js:
+ - 0.10
+
+before_script:
+ - npm install -g grunt-cli
+
+script: grunt build:travis
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9c4db38
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,38 @@
+Submitting Issues
+=================
+
+If you are submitting a bug, please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) demonstrating the issue. Code issues and fringe case bugs that do not include a jsfiddle (or similar) will be closed.
+
+Issues that are submitted without a description (title only) will be closed with no further explanation.
+
+Contributing code
+=================
+
+To contribute, fork the library and install grunt and dependencies. You need [node](http://nodejs.org/); use [nvm](https://github.com/creationix/nvm) or [nenv](https://github.com/ryuone/nenv) to install it.
+
+```bash
+git clone https://github.com/Eonasdan/bootstrap-datetimepicker.git
+cd bootstrap-datetimepicker
+npm install -g grunt-cli
+npm install
+git checkout development # all patches against development branch, please!
+grunt # this runs tests and jshint
+```
+
+Very important notes
+====================
+
+ * **Pull requests to the `master` branch will be closed.** Please submit all pull requests to the `development` branch.
+ * **Do not include the minified files in your pull request.** Don't worry, we'll build them when we cut a release.
+ * Pull requests that do not include a description (title only) and the following will be closed:
+ * What the change does
+ * A use case (for new features or enhancements)
+
+Grunt tasks
+===========
+
+We use Grunt for managing the build. Here are some useful Grunt tasks:
+
+ * `grunt` The default task lints the code and runs the tests. You should make sure you do this before submitting a PR.
+ * `grunt build` Compiles the less stylesheet and minifies the javascript source in build directory.
+ * `grunt build:travis` Compliles and runs the jasmine/travis tests. **All PR's MUST pass tests in place**
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..a13ab2b
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,191 @@
+module.exports = function (grunt) {
+ 'use strict';
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ uglify : {
+ target: {
+ files: {
+ 'build/js/bootstrap-datetimepicker.min.js' : 'src/js/bootstrap-datetimepicker.js'
+ }
+ },
+ options: {
+ mangle: true,
+ compress: {
+ dead_code: false // jshint ignore:line
+ },
+ output: {
+ ascii_only: true // jshint ignore:line
+ },
+ report: 'min',
+ preserveComments: 'some'
+ }
+ },
+
+ jshint: {
+ all: [
+ 'Gruntfile.js', 'src/js/*.js', 'test/*.js'
+ ],
+ options: {
+ 'browser' : true,
+ 'node' : true,
+ 'jquery' : true,
+ 'boss' : false,
+ 'curly' : true,
+ 'debug' : false,
+ 'devel' : false,
+ 'eqeqeq' : true,
+ 'bitwise' : true,
+ 'eqnull' : true,
+ 'evil' : false,
+ 'forin' : true,
+ 'immed' : false,
+ 'laxbreak' : false,
+ 'newcap' : true,
+ 'noarg' : true,
+ 'noempty' : false,
+ 'nonew' : false,
+ 'onevar' : true,
+ 'plusplus' : false,
+ 'regexp' : false,
+ 'undef' : true,
+ 'sub' : true,
+ 'strict' : true,
+ 'unused' : true,
+ 'white' : true,
+ 'es3' : true,
+ 'camelcase' : true,
+ 'quotmark' : 'single',
+ 'globals': {
+ 'define': false,
+ 'moment': false,
+ // Jasmine
+ 'jasmine': false,
+ 'describe': false,
+ 'xdescribe': false,
+ 'expect': false,
+ 'it': false,
+ 'xit': false,
+ 'spyOn': false,
+ 'beforeEach': false,
+ 'afterEach': false
+ }
+ }
+ },
+
+ jscs: {
+ all: [
+ 'Gruntfile.js', 'src/js/*.js', 'test/*.js'
+ ],
+ options: {
+ config: '.jscs.json'
+ }
+ },
+
+ less: {
+ production: {
+ options: {
+ cleancss: true,
+ compress: true,
+ paths: 'node_modules'
+ },
+ files: {
+ 'build/css/bootstrap-datetimepicker.min.css': 'src/less/bootstrap-datetimepicker-build.less'
+ }
+ },
+ development: {
+ options: {
+ paths: 'node_modules'
+ },
+ files: {
+ 'build/css/bootstrap-datetimepicker.css': 'src/less/bootstrap-datetimepicker-build.less'
+ }
+ }
+ },
+
+ jasmine: {
+ customTemplate: {
+ src: 'src/js/*.js',
+ options: {
+ specs: 'test/*Spec.js',
+ helpers: 'test/*Helper.js',
+ styles: [
+ 'node_modules/bootstrap/dist/css/bootstrap.min.css',
+ 'build/css/bootstrap-datetimepicker.min.css'
+ ],
+ vendor: [
+ 'node_modules/jquery/dist/jquery.min.js',
+ 'node_modules/moment/min/moment-with-locales.min.js',
+ 'node_modules/bootstrap/dist/js/bootstrap.min.js'
+ ],
+ display: 'none',
+ summary: 'true'
+ }
+ }
+ },
+
+ nugetpack: {
+ less: {
+ src: 'src/nuget/Bootstrap.v3.Datetimepicker.nuspec',
+ dest: 'build/nuget',
+ options: {
+ version: '<%= pkg.version %>'
+ }
+ },
+ css: {
+ src: 'src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec',
+ dest: 'build/nuget',
+ options: {
+ version: '<%= pkg.version %>'
+ }
+ }
+ }
+ });
+
+ grunt.loadTasks('tasks');
+
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
+ grunt.loadNpmTasks('grunt-nuget');
+
+ // These plugins provide necessary tasks.
+ require('load-grunt-tasks')(grunt);
+
+ // Default task.
+ grunt.registerTask('default', ['jshint', 'jscs', 'less', 'jasmine']);
+
+ // travis build task
+ grunt.registerTask('build:travis', [
+ // code style
+ 'jshint', 'jscs',
+ // build
+ 'uglify', 'less',
+ // tests
+ 'jasmine'
+ ]);
+
+ // Task to be run when building
+ grunt.registerTask('build', [
+ 'jshint', 'jscs', 'uglify', 'less'
+ ]);
+
+ grunt.registerTask('test', ['jshint', 'jscs', 'uglify', 'less', 'jasmine']);
+
+ grunt.registerTask('docs', 'Generate docs', function () {
+ grunt.util.spawn({
+ cmd: 'mkdocs',
+ args: ['build', '--clean']
+ });
+ });
+
+ grunt.registerTask('release', function (version) {
+ if (!version || version.split('.').length !== 3) {
+ grunt.fail.fatal('malformed version. Use grunt release:1.2.3');
+ }
+
+ grunt.task.run([
+ 'bump_version:' + version,
+ 'build:travis',
+ 'docs'
+ ]);
+ });
+};
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f8071d7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jonathan Peterson (@Eonasdan)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e63012c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# Bootstrap 3 Date/Time Picker
+![GitHub version](https://badge.fury.io/gh/Eonasdan%2Fbootstrap-datetimepicker.png) ![Travis](https://travis-ci.org/Eonasdan/bootstrap-datetimepicker.svg?branch=development) [![Stories in Ready](https://badge.waffle.io/Eonasdan/bootstrap-datetimepicker.png?label=ready&title=Ready)](https://waffle.io/Eonasdan/bootstrap-datetimepicker)
+
+![DateTimePicker](http://i.imgur.com/nfnvh5g.png)
+
+## [View the manual and demos](http://eonasdan.github.io/bootstrap-datetimepicker/)
+
+# v4.17.37
+
+Is the current stable release. The latest and greatest changes are available from the [development branch](https://github.com/Eonasdan/bootstrap-datetimepicker/tree/development)
+
+# Version 5
+
+IMPORANT! The Nuget packages will be depreciated in this release. Moving forward, Asp.Net/Nuget will **NOT** be delivering content packages like this one and you will need to use bower. See [this issue for more]( https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1128)
+
+v5 is [in planning](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/841). We welcome your suggestions.
+
+A major update is a great time to make be able to make breaking changes.
+
+## Submitting Issues
+If you have issues, please check the following first:
+* Have you read the docs?
+* Do you have the latest version of momentjs?
+* Do you have the latest version of jQuery?
+* Please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) with an example of your issue before you post an issue here.
+* Please indicate which version of the picker you are using (this can be found at the top of any included file)
+
+## Where do you use this?
+I'd love to know if your public site is using this plugin and list your logo on the documentation site. Please email me `me at eonasdan dot com`.
+
+##Priority support is available at an hourly rate.
+
+If you have an urgent request, bug or need installation help, please contact me at `me at eonasdan dot com` for a quote.
+
+## [Installation instructions](http://eonasdan.github.io/bootstrap-datetimepicker/Installing/)
+
+## [Change Log](http://eonasdan.github.io/bootstrap-datetimepicker/Changelog/)
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..208176a
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "eonasdan-bootstrap-datetimepicker",
+ "version": "4.17.37",
+ "main": [
+ "build/css/bootstrap-datetimepicker.min.css",
+ "build/js/bootstrap-datetimepicker.min.js"
+ ],
+ "dependencies": {
+ "jquery": ">=1.8.3",
+ "moment": ">=2.9.0",
+ "moment-timezone": ">=0.4.0"
+ },
+ "homepage": "https://github.com/Eonasdan/bootstrap-datetimepicker",
+ "authors": [
+ "Eonasdan"
+ ],
+ "description": "bootstrap3 datetimepicker",
+ "keywords": [
+ "twitter-bootstrap",
+ "bootstrap",
+ "datepicker",
+ "datetimepicker",
+ "timepicker",
+ "moment"
+ ],
+ "license": "MIT",
+ "private": false,
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
diff --git a/build/css/bootstrap-datetimepicker-standalone.css b/build/css/bootstrap-datetimepicker-standalone.css
new file mode 100644
index 0000000..49e3ea6
--- /dev/null
+++ b/build/css/bootstrap-datetimepicker-standalone.css
@@ -0,0 +1,103 @@
+/*!
+ * Datetimepicker for Bootstrap 3
+ * version : 4.17.37
+ * https://github.com/Eonasdan/bootstrap-datetimepicker/
+ */
+ at font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('../fonts/glyphicons-halflings-regular.eot');
+ src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.glyphicon-time:before {
+ content: "\e023";
+}
+
+.glyphicon-chevron-left:before {
+ content: "\e079";
+}
+
+.glyphicon-chevron-right:before {
+ content: "\e080";
+}
+
+.glyphicon-chevron-up:before {
+ content: "\e113";
+}
+
+.glyphicon-chevron-down:before {
+ content: "\e114";
+}
+
+.glyphicon-calendar:before {
+ content: "\e109";
+}
+
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.collapse {
+ display: none;
+}
+
+ .collapse.in {
+ display: block;
+ }
+
+.dropdown-menu {
+ position: absolute;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 14px;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, .15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
\ No newline at end of file
diff --git a/build/css/bootstrap-datetimepicker.css b/build/css/bootstrap-datetimepicker.css
new file mode 100644
index 0000000..b8bf43b
--- /dev/null
+++ b/build/css/bootstrap-datetimepicker.css
@@ -0,0 +1,373 @@
+/*!
+ * Datetimepicker for Bootstrap 3
+ * version : 4.17.37
+ * https://github.com/Eonasdan/bootstrap-datetimepicker/
+ */
+.bootstrap-datetimepicker-widget {
+ list-style: none;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu {
+ margin: 2px 0;
+ padding: 4px;
+ width: 19em;
+}
+ at media (min-width: 768px) {
+ .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+ width: 38em;
+ }
+}
+ at media (min-width: 992px) {
+ .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+ width: 38em;
+ }
+}
+ at media (min-width: 1200px) {
+ .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+ width: 38em;
+ }
+}
+.bootstrap-datetimepicker-widget.dropdown-menu:before,
+.bootstrap-datetimepicker-widget.dropdown-menu:after {
+ content: '';
+ display: inline-block;
+ position: absolute;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #cccccc;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ top: -7px;
+ left: 7px;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid white;
+ top: -6px;
+ left: 8px;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-top: 7px solid #cccccc;
+ border-top-color: rgba(0, 0, 0, 0.2);
+ bottom: -7px;
+ left: 6px;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid white;
+ bottom: -6px;
+ left: 7px;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
+ left: auto;
+ right: 6px;
+}
+.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
+ left: auto;
+ right: 7px;
+}
+.bootstrap-datetimepicker-widget .list-unstyled {
+ margin: 0;
+}
+.bootstrap-datetimepicker-widget a[data-action] {
+ padding: 6px 0;
+}
+.bootstrap-datetimepicker-widget a[data-action]:active {
+ box-shadow: none;
+}
+.bootstrap-datetimepicker-widget .timepicker-hour,
+.bootstrap-datetimepicker-widget .timepicker-minute,
+.bootstrap-datetimepicker-widget .timepicker-second {
+ width: 54px;
+ font-weight: bold;
+ font-size: 1.2em;
+ margin: 0;
+}
+.bootstrap-datetimepicker-widget button[data-action] {
+ padding: 6px;
+}
+.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Increment Hours";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Increment Minutes";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Decrement Hours";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Decrement Minutes";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Show Hours";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Show Minutes";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Toggle AM/PM";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Clear the picker";
+}
+.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Set the date to today";
+}
+.bootstrap-datetimepicker-widget .picker-switch {
+ text-align: center;
+}
+.bootstrap-datetimepicker-widget .picker-switch::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Toggle Date and Time Screens";
+}
+.bootstrap-datetimepicker-widget .picker-switch td {
+ padding: 0;
+ margin: 0;
+ height: auto;
+ width: auto;
+ line-height: inherit;
+}
+.bootstrap-datetimepicker-widget .picker-switch td span {
+ line-height: 2.5;
+ height: 2.5em;
+ width: 100%;
+}
+.bootstrap-datetimepicker-widget table {
+ width: 100%;
+ margin: 0;
+}
+.bootstrap-datetimepicker-widget table td,
+.bootstrap-datetimepicker-widget table th {
+ text-align: center;
+ border-radius: 4px;
+}
+.bootstrap-datetimepicker-widget table th {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+}
+.bootstrap-datetimepicker-widget table th.picker-switch {
+ width: 145px;
+}
+.bootstrap-datetimepicker-widget table th.disabled,
+.bootstrap-datetimepicker-widget table th.disabled:hover {
+ background: none;
+ color: #777777;
+ cursor: not-allowed;
+}
+.bootstrap-datetimepicker-widget table th.prev::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Previous Month";
+}
+.bootstrap-datetimepicker-widget table th.next::after {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ content: "Next Month";
+}
+.bootstrap-datetimepicker-widget table thead tr:first-child th {
+ cursor: pointer;
+}
+.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
+ background: #eeeeee;
+}
+.bootstrap-datetimepicker-widget table td {
+ height: 54px;
+ line-height: 54px;
+ width: 54px;
+}
+.bootstrap-datetimepicker-widget table td.cw {
+ font-size: .8em;
+ height: 20px;
+ line-height: 20px;
+ color: #777777;
+}
+.bootstrap-datetimepicker-widget table td.day {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+}
+.bootstrap-datetimepicker-widget table td.day:hover,
+.bootstrap-datetimepicker-widget table td.hour:hover,
+.bootstrap-datetimepicker-widget table td.minute:hover,
+.bootstrap-datetimepicker-widget table td.second:hover {
+ background: #eeeeee;
+ cursor: pointer;
+}
+.bootstrap-datetimepicker-widget table td.old,
+.bootstrap-datetimepicker-widget table td.new {
+ color: #777777;
+}
+.bootstrap-datetimepicker-widget table td.today {
+ position: relative;
+}
+.bootstrap-datetimepicker-widget table td.today:before {
+ content: '';
+ display: inline-block;
+ border: solid transparent;
+ border-width: 0 0 7px 7px;
+ border-bottom-color: #337ab7;
+ border-top-color: rgba(0, 0, 0, 0.2);
+ position: absolute;
+ bottom: 4px;
+ right: 4px;
+}
+.bootstrap-datetimepicker-widget table td.active,
+.bootstrap-datetimepicker-widget table td.active:hover {
+ background-color: #337ab7;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.bootstrap-datetimepicker-widget table td.active.today:before {
+ border-bottom-color: #fff;
+}
+.bootstrap-datetimepicker-widget table td.disabled,
+.bootstrap-datetimepicker-widget table td.disabled:hover {
+ background: none;
+ color: #777777;
+ cursor: not-allowed;
+}
+.bootstrap-datetimepicker-widget table td span {
+ display: inline-block;
+ width: 54px;
+ height: 54px;
+ line-height: 54px;
+ margin: 2px 1.5px;
+ cursor: pointer;
+ border-radius: 4px;
+}
+.bootstrap-datetimepicker-widget table td span:hover {
+ background: #eeeeee;
+}
+.bootstrap-datetimepicker-widget table td span.active {
+ background-color: #337ab7;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.bootstrap-datetimepicker-widget table td span.old {
+ color: #777777;
+}
+.bootstrap-datetimepicker-widget table td span.disabled,
+.bootstrap-datetimepicker-widget table td span.disabled:hover {
+ background: none;
+ color: #777777;
+ cursor: not-allowed;
+}
+.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
+ height: 27px;
+ line-height: 27px;
+}
+.bootstrap-datetimepicker-widget.wider {
+ width: 21em;
+}
+.bootstrap-datetimepicker-widget .datepicker-decades .decade {
+ line-height: 1.8em !important;
+}
+.input-group.date .input-group-addon {
+ cursor: pointer;
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
diff --git a/build/css/bootstrap-datetimepicker.min.css b/build/css/bootstrap-datetimepicker.min.css
new file mode 100644
index 0000000..63c2a3a
--- /dev/null
+++ b/build/css/bootstrap-datetimepicker.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Datetimepicker for Bootstrap 3
+ * version : 4.17.37
+ * https://github.com/Eonasdan/bootstrap-datetimepicker/
+ */.bootstrap-datetimepicker-widget{list-style:none}.bootstrap-datetimepicker-widget.dropdown-menu{margin:2px 0;padding:4px;width:19em}@media (min-width:768px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:992px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:1200px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}.bootstrap-datetimepicker-widget.dropdown-menu:before,.boot [...]
\ No newline at end of file
diff --git a/build/js/bootstrap-datetimepicker.min.js b/build/js/bootstrap-datetimepicker.min.js
new file mode 100644
index 0000000..db3d085
--- /dev/null
+++ b/build/js/bootstrap-datetimepicker.min.js
@@ -0,0 +1,9 @@
+/*! version : 4.17.37
+ =========================================================
+ bootstrap-datetimejs
+ https://github.com/Eonasdan/bootstrap-datetimepicker
+ Copyright (c) 2015 Jonathan Peterson
+ =========================================================
+ */
+!function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be lo [...]
+if("function"!=typeof a)throw new TypeError("parseInputDate() sholud be as function");return d.parseInputDate=a,l},l.disabledTimeIntervals=function(b){if(0===arguments.length)return d.disabledTimeIntervals?a.extend({},d.disabledTimeIntervals):d.disabledTimeIntervals;if(!b)return d.disabledTimeIntervals=!1,$(),l;if(!(b instanceof Array))throw new TypeError("disabledTimeIntervals() expects an array parameter");return d.disabledTimeIntervals=b,$(),l},l.disabledHours=function(b){if(0===argum [...]
\ No newline at end of file
diff --git a/component.json b/component.json
new file mode 100644
index 0000000..dfc00ee
--- /dev/null
+++ b/component.json
@@ -0,0 +1,9 @@
+{
+ "name": "bootstrap-datetimepicker",
+ "version": "4.17.37",
+ "main": ["build/css/bootstrap-datetimepicker.min.css","build/js/bootstrap-datetimepicker.min.js"],
+ "dependencies": {
+ "jquery" : ">=1.8.3",
+ "moment": ">=2.9.0"
+ }
+}
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..c05f346
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "eonasdan/bootstrap-datetimepicker",
+ "type": "component",
+ "version": "4.17.37",
+ "description": "Date/time picker widget based on twitter bootstrap",
+ "keywords": [
+ "bootstrap",
+ "datetimepicker"
+ ],
+ "homepage": "http://eonasdan.github.io/bootstrap-datetimepicker/",
+ "license": "MIT",
+ "require": {
+ "robloach/component-installer": "*",
+ "components/jquery": ">=1.9.1",
+ "moment/moment": ">=2.8"
+ },
+ "extra": {
+ "component": {
+ "scripts": [
+ "src/js/bootstrap-datetimepicker.js"
+ ],
+ "files": [
+ "build/js/bootstrap-datetimepicker.min.js",
+ "build/css/bootstrap-datetimepicker.min.css"
+ ]
+ }
+ }
+}
diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md
new file mode 100644
index 0000000..01ce0ed
--- /dev/null
+++ b/docs/ChangeLog.md
@@ -0,0 +1,222 @@
+# Version 4
+
+## 4.17.37
+
+### New Features
+
+* Momentjs TZ intergration #1242 thanks @bodrick
+* Independent CSS file, in case you don't want bootstrap for some reason
+
+### Bug Squashing
+
+* Slight changes decade view
+* Moved all tooltip text to `tooltips`
+* fixed #1212
+
+## 4.15.35
+
+### New Features
+
+`tooltips` allows custom, localized text to be included for icon tooltips
+
+### Bug Squashing
+
+fixed #1066
+
+fixed #1087 `sideBySide` properly supports `toolbarPlacement [top, bottom]`
+
+fixed #1119
+
+fixed #1069 added input.blur()
+
+fixed #1049 fixed doc example
+
+fixed #999 picker now looks for an element with `.input-group-addon`
+
+
+## 4.14.30
+
+### New Features
+
+`disabledTimeIntervals` #644
+
+`allowInputToggle` #929
+
+`focusOnShow` #884
+
+public `viewDate` function #872
+
+`enabledHours` and `disabledHours`.
+
+`dp.update` fires when `viewDate` is changed (in most cases) #937
+
+`viewMode` now supports a decades view.
+
+ **Note**: because the year picker shows 12 years at a time, I've elected to make this view show blocks of 12 years
+
+ **Note**: when selecting a decade the `viewDate` will change to the **center** of the selected years
+
+`parseInputDate` #1095
+
+### Bug Squashing
+
+fixed #815 by adding `.wider` when using both seconds and am/pm.
+
+fixed #816 changed both min/max date to move the selected date inside.
+
+fixed #855 #881 `fillDate`, `fillMonths`, `fillDow` uses `startOf('day')`, which will hopefully fix the DST issues.
+
+fixed #885 `daysOfWeekDisabled` will move the date to a valid date if `useCurrent` is `true`. Today button will check if the DoW is disabled.
+
+fixed #906
+
+fixed #912 if `useCurrent:false` month and year view will no longer have the current month/year selected.
+
+fixed #914 `use24hours` will ignore anything in side of `[]` in the format string.
+
+fixed #916 added titles to all icons. At some point the text should be moved to the icon's array, but this would probably be a breaking change.
+
+fixed #940 added -1 tab index to am/pm selector
+
+### Other Changes
+
+changed in/decrement behavior to check if the new date is valid at that granularity (hours, minutes, seconds). will also validate as before
+
+## 4.7.14
+
+Added several in new features:
+
+ `keybinds`, `inline`, `debug`, `clear()`, `showClose`, `ingoreReadOnly`, `datepickerInput` and `keepInvalid`.
+
+Bug squashing
+
+## 4.0.0
+
+#### Changes for using the component
+
+* Defined a [Public API](https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Version-4-Public-API) and hidden rest of functions, variables so that all configuration options can be changed dynamically.
+
+* `set/getDate()` is now replaced with an overloaded `date()` function. Use it without a parameter to get the currently set date or with a parameter to set the date.
+
+* `hide()`, `show()`, `toggle()`, `enable()`, `disable()` and the rest of setter functions now support chaining. ie `$('#id').data('DateTimePicker').format('DD-MM-YYYY').minDate(moment()).defaultDate(moment()).show()` works
+
+* Replaced previous - next buttons in Date subviews with configurable icons
+
+* Changed `language` option name to `locale` to be inline with moment naming
+
+* Implemented #402 all data-date-* variables are more readable and also match with the ones in the configuration object
+
+* `options.direction` and `options.orientation` were merged into a single object `options.widgetPositioning` with `vertical` and `horizontal` keys that take a string value of `'auto', 'top', 'bottom'` and `'auto', 'left', 'right'` respectively. Note that the `'up'` option was renamed to `'top'`
+
+#### Added functionality
+
+* added a second way to define options as data attributes. Instead of adding distinct `data-date-*` config options you can now also pass a `data-date-options` attribute containing an object just the same as the options object that `element.datetimepicker` constructor call takes
+
+* also added a `options()` public api function to get/set that takes an option object and applies it to the component in one call
+
+* Implemented [#130](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/130) by introducing a boolean `options.calendarWeeks` and `calendarWeeks()` api function
+
+* Implemented [#328](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/328), [#426](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/426)
+
+* Implemented [#432](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/432). Widget DOM element is now lazily added only when shown and removed from the document when hidden.
+
+* Implemented [#141](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/141) and [#283](https://github.com/Eonasdan/bootstrap-datetimepicker/issues/283)
+
+
+#### Contributors related internal code changes
+
+* Refactor all UI click functions and put them as functions in the actions array private variable
+
+* Refactor template building process to seperate functions according to what they do
+
+* Remove some styles that where hardcoded in the javascript code
+
+* Refactor all code that changes the picker.date to change it through the setValue function to allow one place for validation logic (min/max/weekdaysenabled etc) and also one place for emmiting dp.change events
+
+* The v4beta branch code includes all fixes up to v.3.1.2
+
+* Added `toggle()` to the public API which toggles the visibility of the DateTimePicker
+
+* Refactored set function to be included in the setValue function
+
+* Added a testing framework using jasmine and phantom.js
+
+# Version 3
+
+## 3.0.0
+
+
+* Fix for #170, #179, #183: Changed event to `dp.-`. This should fix the double change event firing.
+* Fix for #192: `setDate` now fires `dp.change`
+* Fix for #182: Picker will **not** set the default date if the input field has a value
+* Fix for #169: Seconds doesn't get reset when changing the date (Thanks to PR #174)
+* Fix for #168 z-index fix for BS modal
+* Fix for #155 Picker properly displays the active year and month
+* Fix for #154 CSS update to fix the collapse jump
+* Fix for #150 and #75 `minViewMode` and `viewMode` work properly
+* Fix for #147 AM/PM won't toggle when selecting a value from the hours grid
+* Fix for #44 Finally! It's here!! Thanks to @ruiwei and his code on #210 picker will adjust the positioning of the widget.
+
+#### Manually merged PR
+
+* PR #178 When using `minuteStepping` the minute select grid will only show available steppings
+* PR #195, #197 Using the `data-OPTION` has been changed to `data-date-OPTION`. These options are expected to be on the `input-group` if you're using the `input-group` **or** the a bare input field if you're not using the `input-group`
+* PR #184 The option `sideBySide` change be used to display both the d and the timepicker side by side
+* PR #143 Added option `daysOfWeekDisabled: []`. For example, use `daysOfWeekDisabled: [0,6]` to disable Sunday and Saturday
+
+#### **Other Changes**
+* Changed picker width to 300px if using seconds and am/pm
+* Added option `useCurrent`, thanks to @ruiwei. When true, picker will set the value to the current date/time (respects picker's format)
+* Added option `showToday`, thanks to @ruiwei. When true, picker will display a small arrow to indicate today's date.
+* Changed `startDate` to `minDate` and `endDate` to `maxDate` to make it more clear what these options do.
+
+# Version 2
+
+#### 2.1.32 (Hotfix)
+
+* Fix for #151: When a bad date value or the picker is cleared, the plugin will not longer attempt to reset it back to the previous date
+* Fix for #140: `setDate` can be given `null` to force clear the picker
+
+#### 2.1.30
+##### Important! `build.less` file name has been been changed to `bootstrap-datetimepicker-build.less` to prevent collisions
+
+* Fix for #135: `setStartDate` and `setEndDate` should now properly set.
+* Fix for #133: Typed in date now respects en/disabled dates
+* Fix for #132: En/disable picker function works again
+* Fix for #117, #119, #128, #121: double event `change` event issues should be fixed
+* Fix for #112: `change` function no longer sets the input to a blank value if the passed in date is invalid
+
+* Enhancement for #103: Increated the `z-index` of the widget
+
+#### 2.1.20
+* Fix for #83: Changes to the picker should fire native `change` event for knockout and the like as well as `change.dp` which contains the old date and the new date
+* Fix for #78: Script has been update for breaking changes in Moment 2.4.0
+* Fix for #73: IE8 should be working now
+
+* Enhancement for #79: `minuteStepping` option takes a number (default is 1). Changing the minutes in the time picker will step by this number.
+* Enhancement for #74 and #65: `useMinutes` and `useSeconds` are now options. Disabling seconds will hide the seconds spinner. Disabling minutes will display `00` and hide the arrows
+* Enhancement for #67: Picker will now attempt to convert all `data-OPTION` into its appropriate option
+
+#### 2.1.11
+* Fix for #51, #60
+* Fix for #52: Picker has its own `moment` object since moment 2.4.0 has removed global reference
+* Fix for #57: New option for `useStrict`. When validating dates in `update` and `change`, the picker can use a stricter formatting validation
+* Fix for #61: Picker should now properly take formatted date. Should also have correct start of the week for locales.
+* Fix for #62: Default format will properly validate time picker only.
+
+#### 2.1.5
+* Custom icons, such as Font Awesome, are now supported. (#49)
+* If more then one `input-group-addon` is present use `datepickerbutton` to identify where the picker should popup from. (#48)
+* New Event: `error.dp`. Fires when Moment cannot parse the date or when the timepicker cannot change because of a `disabledDates` setting. Returns a Moment date object. The specific error can be found be using `invalidAt()`. For more information see [Moment's docs](http://momentjs.com/docs/#/parsing/is-valid/)
+* Fix for #42, plugin will now check for `A` or `a` in the format string to determine if the AM/PM selector should display.
+* Fix for #45, fixed null/empty and invalid dates
+* Fix for #46, fixed active date highlighting
+* Fix for #47, `change.dp` event to also include the previous date.
+
+####2.0.1
+* New event `error.dp` fires when plugin cannot parse date or when increase/descreasing hours/minutes to a disabled date.
+* Minor fixes
+
+####2.0.0
+* `disabledDates` is now an option to set the disabled dates. It accepts date objects like `new Date("November 12, 2013 00:00:00")` and `12/25/2013' and `moment` date objects
+* Events are easier to use
\ No newline at end of file
diff --git a/docs/ContributorsGuide.md b/docs/ContributorsGuide.md
new file mode 100644
index 0000000..ed3e9fa
--- /dev/null
+++ b/docs/ContributorsGuide.md
@@ -0,0 +1,126 @@
+This guide is aimed to contributors wishing to understand the internals of the code in order to change/evolve the component.
+
+**Note:** this guide refers to **version 4** which is currently in beta and will be updated as we progress
+
+## Introduction
+This component consists actually of 2 subcomponent UI widgets one for the date and one for the time selection process. The developers can configure which of those are needed and also the granularity that the component will allow the users to select a date/time. Developers also choose the format that the selected datetime will be displayed in the input field.
+The component uses on `jQuery`, `moment.js` and `bootstrap` libraries.
+
+## Code
+### Private variables
+
+* `element` - Holds the DOM element this instance is attached to
+
+* `options` - Holds an object with the curently set options for the specific instance of the component. Don't directly change the properties of that object use the public API methods instead. DO NOT expose this object or its properties outside of the component.
+
+* `picker` - Reference variable to the created instance `(this)`
+
+* `date` - Holds the moment object for the model value of the component. **DON'T** directly change this variable unless you **REALLY** know what you are doing. Use `setValue()` function to set it. It handles all component logic for updating the model value and emitting all the appropriate events
+
+* `viewDate` - Holds the currently selected value that the user has selected through the widget. This is not the model value this is the view value. Changing this usually requires a subsequent call to `update()` function
+
+* `unset` - A `boolean` variable that holds wheather the components model value is set or not. Model's value starts as `unset = true` and if is either set by the user or programmatically through the api to a valid value then it is set to `false`. If subsequent events lead to an invalid value then this variable is set to `true` again. Setting this variable usually takes place in the `setValue()` function.
+
+* `input` - Hold the DOM input element this instance is attached to
+
+* `component` - Holds a reference to the .input-group DOM element that the widget is attached or false if it is attached directly on an input field
+
+* `widget` - Holds a reference to the DOM element containing the widget or `false` if the widget is hidden
+
+* `use24hours` - Holds whether the component uses 24 hours format or not. This is initialized on the `format()` function
+
+* `minViewModeNumber` - Holds the Numeric equivelant of the options.minViewMode parameter
+
+* `format` - Holds the current format string that is used for formating the date model value. Note this is not the same thing as the `options.format` as the second could be set to `false` in which case the first takes the locale's `L` or `LT` value
+
+* `currentViewMode` - Hold the state of the current viewMode for the DatePicker subcomponent
+
+* `actions` - An object containing all the functions that can be called when the users clicks on the widget
+
+* `datePickerModes` - An array of objects with configuration parameters for the different views of the DatePicker subcomponent
+
+* `viewModes` - An array of strings containing all the possible strings that `options.viewMode` can take through `viewMode()` public api function
+
+* `directionModes` - An array of strings containing all the possible strings that `options.direction` can take through `direction()` public api function
+
+* `orientationModes` - An array of strings containing all the possible strings that `options.orientation` can take through `orientation()` public api function
+
+### Private functions
+
+#### Widget related
+
+* `getDatePickerTemplate()` - returns a string containing the html code for the date picker subcomponent
+
+* `getTimePickerTemplate()` - returns a string containing the html code for the time picker subcomponent
+
+* `getTemplate()` - returns a string with containing the html code for all the DateTimePicker component
+
+* `place()` - handles the placement of the widget's dropdown
+
+* `updateMonths()` - updates the html subpage related to the months for Date picker view
+
+* `updateYears()` - updates the html subpage related to the years for Date picker view
+
+* `fillDate()` - updates the html subpage related to the days for Date picker view
+
+* `fillHours()` - Creates the hours spans for the hours subview of the Time subcomponent
+
+* `fillMinutes()` - Creates the minutes spans for the hours subview of the Time subcomponent
+
+* `fillSeconds()` - Creates the seconds spans for the hours subview of the Time subcomponent
+
+* `fillTime()` - Creates the main subview of the Time subcomponent
+
+* `update()` - updates the UI of part of the widget
+
+* `fillDow()` - Creates the day names in the days subview on the Date subcomponent
+
+* `fillMonths()` - Creates the month spans for the months subview of the Date subcomponent
+
+* `createWidget()` - creates the UI widget end attaches widget event listeners
+
+* `destroyWidget()` - destroys the UI widget DOM element and detaches widget event listeners
+
+* `showMode(dir)` - toggles between the various subpage related views of the DateTimePicker
+
+#### Events related
+
+* `notifyEvent(e)` - Use this function when you want to send en event to listener this could be used as a filter later
+
+* `stopEvent(e)` - Shortcut for stopping propagation of events
+
+* `doAction(e)` - Proxy function to call all the UI related click events
+
+* `keydown(e)` - Function to trap
+
+* `change(e)` - Listener function to track change events occuring on the `input` dom element the component is attached to
+
+* `attachDatePickerElementEvents()` - Attaches listeners to the existing DOM elements the component is attached to. Called upon construction of each datetimepicker instance
+
+* `detachDatePickerElementEvents()` - Detaches listeners from the DOM element the component is attached to. Called on `destroy()`
+
+* `attachDatePickerWidgetEvents()` - Attaches listeners on the components widget. Called on `show()`
+
+* `detachDatePickerWidgetEvents()` - Detaches listeners on the components widget. Called on `hide()`
+
+#### Model related
+
+* `setValue(targetMoment)` - Sets the model value of the component takes a moment object. An `error` event will be emmited if the `targetMoment` does not pass the configured validations. Otherwise the `date` variable will be set and the relevant events will be fired.
+
+* `isValid(targetMoment, granularity)` - returns `true` if the `targetMoment` moment object is valid according to the components set validation rules (`min/maxDates`, `disabled/enabledDates` and `daysOfWeekDisabled`). You may pass a second variable to check only up the the specific granularity `year, month, day, hour, minute, second`
+
+#### Utilities
+
+* `indexGivenDates (givenDatesArray)` - Function that takes the array from `enabledDates()` and `disabledDates()` public functions and stores them as object keys to enable quick lookup
+
+* `isInEnableDates(date)` - Checks whether if the given moment object exists in the `options.enabledDates` object
+
+* `isInDisableDates(date)` - Checks whether if the given moment object exists in the `options.disabledDates` array
+
+* `dataToOptions()` - Parses `data-date-*` options set on the input dom element the component is attached to and returns an object with them
+
+* `isInFixed()` - Checks if the dom element or its parents has a fixed position css rule.
+
+* `parseInputDate(date)` - Parses a date parameter with moment using the component's `options.format` and `options.useStrict`. It returns a `moment` object or false if `parsedMoment#isValid()` returns `false`. Use this to parse date inputs from outside the component (public API calls).
+
+* `init()` - Initializes the component. Called when the component instance is created
diff --git a/docs/Events.md b/docs/Events.md
new file mode 100644
index 0000000..295240d
--- /dev/null
+++ b/docs/Events.md
@@ -0,0 +1,96 @@
+## Events
+
+### dp.hide
+
+Fired when the widget is hidden.
+
+Parameters:
+
+```
+e = {
+ date //the currently set date. Type: moment object (clone)
+}
+```
+
+Emitted from:
+
+* toggle()
+* hide()
+* disable()
+
+----------------------
+
+### dp.show
+
+Fired when the widget is shown.
+
+Parameters:
+
+No parameters are include, listen to `dp.change` instead
+
+Emitted from:
+
+* toggle()
+* show()
+
+----------------------
+
+### dp.change
+
+Fired when the date is changed.
+
+Parameters:
+
+```
+e = {
+ date, //date the picker changed to. Type: moment object (clone)
+ oldDate //previous date. Type: moment object (clone) or false in the event of a null
+}
+```
+
+Emitted from:
+
+* toggle() **Note**: Only fired when using `useCurrent`
+* show() **Note**: Only fired when using `useCurrent` or when or the date is changed to comply with date rules (min/max etc)
+* date(newDate)
+* minDate(minDate)
+* maxDate(maxDate)
+* daysOfWeekDisabled()
+
+----------------------
+
+### dp.error
+
+Fired when a selected date fails to pass validation.
+
+Parameters:
+
+```
+e = {
+ date //the invalid date. Type: moment object (clone)
+}
+```
+
+Emmited from:
+
+* minDate(minDate)
+* maxDate(maxDate)
+* daysOfWeekDisabled()
+* setValue() *private function*
+
+----------------------
+
+### dp.update
+
+<small>4.14.30</small>
+
+Fired (in most cases) when the `viewDate` changes. E.g. Next and Previous buttons, selecting a year.
+
+Parameters:
+
+```
+e = {
+ change, //Change type as a momentjs format token. Type: string e.g. yyyy on year change
+ viewDate //new viewDate. Type: moment object
+}
+```
\ No newline at end of file
diff --git a/docs/Extras.md b/docs/Extras.md
new file mode 100644
index 0000000..d830185
--- /dev/null
+++ b/docs/Extras.md
@@ -0,0 +1,95 @@
+# Extras
+
+Guides for making the picker work better with rails, IE, etc
+
+## Rails 3
+
+by [dhulihan](https://github.com/dhulihan)
+
+You can easily override the default rails form helpers (`date_select` and `datetime_select`) with bootstrap-datetimepicker for a much nicer experience.
+
+```rb
+# Add to config/initializers/form.rb or the end of app/helpers/application_helper.rb
+module ActionView
+ module Helpers
+ class FormBuilder
+ def date_select(method, options = {}, html_options = {})
+ existing_date = @object.send(method)
+ formatted_date = existing_date.to_date.strftime("%F") if existing_date.present?
+ @template.content_tag(:div, :class => "input-group") do
+ text_field(method, :value => formatted_date, :class => "form-control datepicker", :"data-date-format" => "YYYY-MM-DD") +
+ @template.content_tag(:span, @template.content_tag(:span, "", :class => "glyphicon glyphicon-calendar") ,:class => "input-group-addon")
+ end
+ end
+
+ def datetime_select(method, options = {}, html_options = {})
+ existing_time = @object.send(method)
+ formatted_time = existing_time.to_time.strftime("%F %I:%M %p") if existing_time.present?
+ @template.content_tag(:div, :class => "input-group") do
+ text_field(method, :value => formatted_time, :class => "form-control datetimepicker", :"data-date-format" => "YYYY-MM-DD hh:mm A") +
+ @template.content_tag(:span, @template.content_tag(:span, "", :class => "glyphicon glyphicon-calendar") ,:class => "input-group-addon")
+ end
+ end
+ end
+ end
+end
+```
+
+The time format used here is ActiveRecord-friendly, which means it will be parsed correctly when passed in through `params` to your record.
+
+That's all there is to it! Now all of your forms that use `datetime_select` or `date_select` will be automatically updated:
+
+```erb
+<% form_for @post do |f| %>
+ <div class="form-group">
+ <label>Published At</label>
+ <%= f.datetime_select :published_at %>
+ </div>
+<% end %>
+```
+
+## IE 7
+
+by [EquilibriumCST](https://github.com/EquilibriumCST)
+
+I succeed to run this widget under IE7.
+Here is what I did.
+
+1. gliphicons are not working under IE7 so add [this css file](https://github.com/coliff/bootstrap-ie7). And this enables the icons.
+
+2. Z-index problem with IE 7. I added position: relative and `z-index: 10` to the parent container. Otherwise popup is shown under the next elements.
+
+3. JS events were not working well.
+
+If you open the datetimepicker widget and click on some button or date inside it, widget is automatically closed.
+So I added `debug: true` as an option when initializing the widget. Why I did this? I saw on line 1121 from bootsrap-datetimepicker.js the code `'blur': options.debug ? '' : hide`.
+And now widget window is not closed on every click inside it, but now you can't close it anyway :)
+And closing should be done manually. I've added this document click handler. If you click something outside the widget, now closing works.
+
+```
+$(document).click(function(e){
+ var target = $(e.target);
+ if(target.parents('.bootstrap-datetimepicker-widget').length < 1 && !target.hasClass('datetimepickerInput') && !target.hasClass('datepickerIcon') && !target.hasClass('clockpickerIcon')){
+ if($('.bootstrap-datetimepicker-widget').length > 0){
+ $('#startDate').data('DateTimePicker').hide();
+ $('#startTime').data('DateTimePicker').hide();
+ $('.datetimepickerInput').blur();
+ }
+ }
+ });
+```
+
+
+But if you have more than one widget on the page like I did, clicking on one widget does'n close the other. Added below lines and now all works fine.
+
+```
+$('#widget1').on("dp.show",function (e) {
+ $('#widget2).data('DateTimePicker').hide();
+});
+
+$('#widget2').on("dp.show",function (e) {
+ $('#widget1).data('DateTimePicker').hide();
+});
+```
+
+I hope this will help to the others who are fighting with the old IE versions :)
\ No newline at end of file
diff --git a/docs/FAQ.md b/docs/FAQ.md
new file mode 100644
index 0000000..82cc4d5
--- /dev/null
+++ b/docs/FAQ.md
@@ -0,0 +1,22 @@
+# FAQs
+
+# How do I disable the date or time element
+<small>How do I format ...; How do I add seconds; etc.</small>
+
+The picker uses the `format` option to decide what components to show. Set `format` to `LT`, `LTS` or another valid [MomentJs format string](http://momentjs.com/docs/#/displaying/format/) to display certain components
+
+# How do I change the language/locale
+
+The picker uses MomentJs to determine the language string. You can use `moment-with-locales` or you can include whatever local file you need. Set the picker's `locale` option to `de` or whatever the locale string is.
+
+# How do I change the styles? The picker closes.
+
+Set `debug:true` which will force the picker to stay open, even `onBlur`. You can hide the picker manually by calling `hide()`
+
+# How do I change the start of the week?
+
+Start of the week is based on the [`locale` provided](Options.md#locale). This is defined by moment's locales. If you want to change it, create your own locale file or override. [See moment's docs](http://momentjs.com/docs/#/i18n/).
+
+# How I use the picker as birthday picker?
+
+Use the [`viewMode`](Options.md#viewmode) option to `'years'`
\ No newline at end of file
diff --git a/docs/Functions.md b/docs/Functions.md
new file mode 100644
index 0000000..980874d
--- /dev/null
+++ b/docs/Functions.md
@@ -0,0 +1,670 @@
+## Functions
+
+<div class="alert alert-info">
+ <strong>Note</strong>
+ All functions are accessed via the <code>data</code> attribute e.g. <code>$('#datetimepicker').data("DateTimePicker").FUNCTION()</code>
+</div>
+
+###destroy()
+
+Destroys the widget and removes all attached event listeners
+
+----------------------
+
+### toggle()
+
+Shows or hides the widget
+
+#### Emits
+
+* `dp.hide` - if the widget is hidden after the toggle call
+
+* `dp.show` - if the widget is show after the toggle call
+
+* `dp.change` - if the widget is opened for the first time and the input element is empty and `options.useCurrent != false`
+
+----------------------
+
+### show()
+
+Shows the widget
+
+#### Emits
+
+* `dp.show` - if the widget was hidden before that call
+
+* `dp.change` - if the widget is opened for the first time and the useCurrent is set to true or to a granularity value and the input element the component is attached to has an empty value
+
+----------------------
+
+### hide()
+
+Hides the widget
+
+#### Emits
+
+* `dp.hide` - if the widget was visible before that call
+
+----------------------
+
+### disable()
+
+Disables the input element, the component is attached to, by adding a `disabled="true"` attribute to it. If the widget was visible before that call it is hidden.
+
+#### Emits
+
+* `dp.hide` - if the widget was visible before that call
+
+----------------------
+
+### enable()
+
+Enables the input element, the component is attached to, by removing `disabled` attribute from it.
+
+----------------------
+
+
+### date
+
+####date()
+
+ Returns the component's model current date, a `moment` object or `null` if not set.
+
+####date([newDate])
+
+ Takes `string, Date, moment, null` parameter and sets the components model current moment to it. Passing a `null` value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the `options.format` and `options.useStrict` components configuration.
+
+##### Throws
+
+* `TypeError` - in case the `newDate` cannot be parsed
+
+##### Emits
+
+* `dp.change` - In case `newDate` is different from current moment
+
+### en/disabledDates
+
+#### disabledDates()
+
+Returns an array with the currently set disabled dates on the component.
+
+#### disabledDates(dates)
+
+Takes an `[` `string` or `Date` or `moment` `]` of values and disallows the user to select those days. Setting this takes precedence over `options.minDate`, `options.maxDate` configuration. Also calling this function removes the configuration of options.enabledDates if such exist.
+
+**Note:** These values are matched with `Day` granularity.
+
+----------------------
+
+#### enabledDates()
+
+Returns an array with the currently set enabled dates on the component.
+
+
+#### enabledDates(dates)
+
+Takes an `[` `string` or `Date` or `moment` `]` of values and allows the user to select only from those days. Setting this takes precedence over `options.minDate`, `options.maxDate` configuration. Also calling this function removes the configuration of options.disabledDates if such exist.
+
+**Note:** These values are matched with `Day` granularity.
+
+----------------------
+
+### defaultDate
+
+#### defaultDate()
+
+Returns a `moment` with the `options.defaultDate` option configuration or `false` if not set
+
+#### defaultDate(defaultDate)
+
+Takes a `string, Date, moment, boolean:false`. Will set the picker's inital date. If a `boolean:false` value is passed the `options.defaultDate` parameter is cleared.
+
+* `TypeError` - if the provided date pass validation, including `disabledDates`, `enabledDates`, `minDate`, `maxDate`, and `daysOfWeekDisabled`
+
+* `TypeError` - if the provided date cannot be parsed by momentjs
+
+----------------------
+
+### useCurrent
+
+#### useCurrent()
+
+Returns a `boolean` or `string` with the `options.useCurrent` option configuration
+
+#### useCurrent(boolean or string)
+
+Takes a `boolean` or `string`. If a `boolean` true is passed and the components model moment is not set (either through `setDate` or through a valid value on the input element the component is attached to) then the first time the user opens the datetimepicker widget the value is initialized to the current moment of the action. If a false `boolean` is passed then no initialization happens on the input element. You can select the granularity on the initialized moment by passing one of the [...]
+
+If for example you pass `'day'` to the `setUseCurrent` function and the input field is empty the first time the user opens the datetimepicker widget the input text will be initialized to the current datetime with day granularity (ie if currentTime = `2014-08-10 13:32:33` the input value will be initialized to `2014-08-10 00:00:00`)
+
+**Note:** If the `options.defaultDate` is set or the input element the component is attached to has already a value that takes precedence and the functionality of `useCurrent` is not triggered!
+
+----------------------
+
+### min/maxDate
+
+#### minDate()
+
+Returns the currently set moment of the `options.minDate` or `false` if not set
+
+#### minDate(minDate)
+
+Takes a minDate `string, Date, moment, boolean:false` parameter and disallows the user to select a moment that is before that moment. If a `boolean:false` value is passed the `options.minDate` parameter is cleared and there is no restriction to the miminum moment the user can select.
+
+**Note:** If the minDate parameter is after the currently selected moment the currently selected moment changes to minDate parameter
+
+##### Throws
+
+* `TypeError` - if minDate parameter cannot be parsed using the `options.format` and `options.useStrict` configuration settings
+
+* `TypeError` - if minDate parameter is after `options.maxDate`
+
+##### Emits
+
+* `dp.change` - if the new minDate is after currently selected moment (waiting for #472 to close in order to finalize this part)
+
+* `dp.error` - if the new minDate is after currently selected moment (waiting for #472 to close in order to finalize this part)
+
+----------------------
+
+#### maxDate()
+
+Returns the currently set moment of the `options.maxDate` or `false` if not set
+
+
+#### maxDate(maxDate)
+
+Takes a maxDate `string, Date, moment, boolean:false` parameter and disallows the user to select a moment that is after that moment. If a `boolean:false` value is passed `options.maxDate` is cleared and there is no restriction to the maximum moment the user can select.
+
+**Note:** If maxDate is before the currently selected moment the currently selected moment changes to maxDate
+
+##### Throws
+
+* `TypeError` - if maxDate parameter cannot be parsed using the `options.format` and `options.useStrict` configuration settings
+
+* `TypeError` - if maxDate parameter is before `options.minDate`
+
+##### Emits
+
+* `dp.change` - if the new maxDate is after currently selected moment (waiting for #472 to close in order to finalize this part)
+
+* `dp.error` - if the new maxDate is after currently selected moment (waiting for #472 to close in order to finalize this part)
+
+----------------------
+
+### daysOfWeekDisabled
+
+#### daysOfWeekDisabled()
+
+Returns an array with the `options.daysOfWeekDisabled` configuration setting of the component.
+
+#### daysOfWeekDisabled(daysOfWeek)
+
+Takes an `[` `Number`:`0` to `6` `]` and disallow the user to select weekdays that exist in this array. This has lower priority over the `options.minDate`, `options.maxDate`, `options.disabledDates` and `options.enabledDates` configuration settings.
+
+##### Emits
+
+* `dp.change` - if the currently selected moment falls in the values passed on the daysOfWeek parameter. (waiting for #472 to close in order to finalize this part)
+
+* `dp.error` - if the currently selected moment falls in the values passed on the daysOfWeek parameter. (waiting for #472 to close in order to finalize this part)
+
+----------------------
+
+### options
+
+####options()
+
+Returns the components current options object. Note that the changing the values of the returned object does not change the components actual configuration. Use `options(options)` to set the components options massively or the other methods for setting config options individually.
+
+
+#### options([options])
+
+Takes an object variable with option key:value properties and configures the component. Use this to update multiple options on the component.
+
+----------------------
+
+### format
+
+#### format()
+
+Returns the component's `options.format` `string`
+
+
+#### format(format)
+
+Takes a [moment.js](http://momentjs.com/docs/#/displaying/format/) format `string` and sets the components `options.format`. This is used for displaying and also for parsing input strings either from the input element the component is attached to or the `date()` function.
+The parameter can also be a `boolean:false` in which case the format is set to the locale's `L LT`.
+
+**Note:** this is also used to determine if the TimePicker sub component will display the hours in 12 or 24 format. (if 'a' or 'h' exists in the passed `string` then a 12 hour mode is set)
+
+----------------------
+
+### extraFormats
+
+#### extraFormats()
+
+Returns a `boolean` or array with the `options.extraFormats` option configuration
+
+#### extraFormats(formats)
+
+Takes an array of valid input moment format options. See PR #666
+
+----------------------
+
+### locale
+
+#### locale()
+
+Returns the currently set locale of the `options.locale`
+
+#### locale(newLocale)
+
+Takes a `string` of any valid [moment locale](https://github.com/moment/moment/tree/develop/locale) e.g. `de` for German.
+
+##### Throws
+
+* `TypeError` - if the locale is not loaded via a separate script or `moment-with-locales`
+
+----------------------
+
+### stepping
+
+#### stepping()
+
+Returns a `number` with the `options.stepping` option configuration
+
+
+#### stepping(number)
+
+Takes a `number`. This be the amount the up/down arrows move the minute value with a time picker.
+
+----------------------
+
+### sideBySide and collapse
+
+#### sideBySide()
+
+Returns a `boolean` of the `options.sideBySide`.
+
+
+#### sideBySide(sideBySide)
+
+Takes a `boolean`. If `sideBySide` is `true` and the time picker is used, both components will display side by side instead of collapsing.
+
+----------------------
+
+#### collapse()
+
+Returns a `boolean` with the `options.collapse` option configuration
+
+#### collapse(collapse)
+
+Takes a `boolean`. If set to `false` the picker will display similar to `sideBySide` except vertical.
+
+----------------------
+
+### icons
+
+#### icons()
+
+Returns an `Ojbect` of `options.icons`
+
+#### icons(icons)
+
+Takes an `Ojbect` of `strings`.
+
+##### Throws
+
+* `TypeError` - if icons parameter is not an `Ojbect`
+
+----------------------
+
+### useStrict
+
+#### useStrict()
+
+Returns a `boolean` of the `options.useStrict`
+
+#### useStrict(useStrict)
+
+Takes a `boolean`. If `useStrict` is `true`, momentjs parsing rules will be stricter when determining if a date is valid or not.
+
+----------------------
+
+### widgetPositioning
+
+#### widgetPositioning()
+
+Returns the currently set `options.widgetPositioning` object containing two keys `horizontal` and `vertical`
+
+
+#### widgetPositioning(positioningObject)
+
+Takes an object parameter that can contain two keys `vertical` and `horizontal` each having a value of `'auto', 'top', 'bottom'` for `vertical` and `'auto', 'left', 'right'` for `horizontal` which defines where the dropdown with the widget will appear relative to the input element the component is attached to.
+
+`'auto'` is the default value for both `horizontal` and `vertical` keys and it tries to automatically place the dropdown in a position that is visible to the user. Usually you should not override those options unless you have a special need in your layout.
+
+----------------------
+
+### viewMode
+
+#### viewMode()
+
+Returns a `string` of the `options.viewMode`.
+
+#### viewMode(newViewMode)
+
+Takes a `string`. Valid values are `'days'`, `'months'`, `'years'` and `'decades'`
+
+##### Throws
+
+* `TypeError` - if `newViewMode` parameter is not an a `string` or if `newViewMode` is not a valid value
+
+----------------------
+
+### calendarWeeks
+
+#### calendarWeeks()
+
+Returns a `boolean` with the current `options.calendarWeeks` option configuration
+
+#### calendarWeeks(boolean)
+
+Takes a `boolean` variable to set if the week numbers will appear to the left on the days view
+
+----------------------
+
+### showClear
+
+#### showClear()
+
+Returns a `boolean` variable with the currently set `options.showClear` option.
+
+#### showClear(boolean)
+
+Takes a `boolean` variable to set if the clear date button will appear on the widget
+
+----------------------
+
+### showTodayButton
+
+#### showTodayButton()
+
+Returns a `boolean` variable with the currently set `options.showTodayButton` option.
+
+
+#### showTodayButton(boolean)
+
+Takes a `boolean` variable to set if the Today button will appear on the widget
+
+----------------------
+
+### toolbarplacement
+
+#### toolbarplacement()
+
+Returns a `string` variable with the currently set `options.toolbarplacement` option.
+
+#### toolbarplacement(string)
+
+Takes a `string` value. Valid values are `'default'`, `'top'` and `'bottom'`.
+
+Changes the placement of the toolbar where the today, clear, component switch icon are located.
+
+----------------------
+
+### dayViewHeaderFormat
+
+#### dayViewHeaderFormat()
+
+Returns a `string` variable with the currently set `options.dayViewHeaderFormat` option.
+
+#### dayViewHeaderFormat(string)
+
+Takes a `string` value.
+
+Used to customize the header of the day view.
+
+----------------------
+
+### keyBinds
+
+#### keyBinds()
+
+Returns a `string` variable with the currently set `options.keyBinds` option.
+
+#### keyBinds(object)
+
+Takes an `object` value.
+
+Allows for several keyBinding functions to be specified for ease of access or accessibility. See the options page for defaults.
+
+----------------------
+
+### clear()
+
+Clears the datepicker by setting the value to `null`
+
+----------------------
+
+### inline
+
+#### inline()
+
+Returns a `boolean` variable with the currently set `options.inline` option.
+
+#### inline(boolean)
+
+Takes a `boolean` value.
+
+Used to customize the header of the day view.
+
+----------------------
+
+### ignoreReadonly
+
+#### ignoreReadonly()
+
+Returns a `boolean` variable with the currently set `options.ignoreReadonly` option.
+
+#### ignoreReadonly(boolean)
+
+Takes a `boolean` value.
+
+Set this to `true` to allow the picker to be used even if the input field is `readonly`. This will **not** bypass the `disabled` property
+
+----------------------
+
+### showClose
+
+#### showClose()
+
+Returns a `boolean` variable with the currently set `options.showClose` option.
+
+#### showClose(boolean)
+
+Takes a `boolean` value.
+
+If `true`, an icon will be displayed on the toolbar that will hide the picker
+
+----------------------
+
+### debug
+
+For the moment this function will only prevent the picker from calling `hide()` on `blur` so that the picker can be inspected.
+
+----------------------
+
+### keepInvalid
+
+<small>4.7.14</small>
+
+#### keepInvalid()
+
+Returns a `string` variable with the currently set `options.keepInvalid` option.
+
+#### keepInvalid(boolean)
+
+Takes a `boolean` value.
+
+If `true`, invalid dates will not be reverted to a previous selection or changed.
+
+----------------------
+
+### allowInputToggle
+
+ <small>4.7.14</small>
+
+#### allowInputToggle()
+
+Returns a `boolean` variable with the currently set `options.allowInputToggle` option.
+
+#### allowInputToggle(boolean)
+
+Takes a `boolean` value.
+
+If `true`, the picker will show on textbox focus and icon click when used in a button group
+
+----------------------
+
+### focusOnShow
+
+<small>4.14.30</small> PR #884
+
+#### focusOnShow()
+
+Returns a `boolean` variable with the currently set `options.focusOnShow` option.
+
+#### focusOnShow(boolean)
+
+Takes a `boolean` value.
+
+If `false`, the textbox will not be given focus when the picker is shown
+
+
+----------------------
+
+### disabledTimeIntervals
+
+<small>4.14.30</small> Issue: #644
+
+#### disabledTimeIntervals()
+
+Returns an `array` variable with the currently set `options.disabledTimeIntervals` option.
+
+#### disabledTimeIntervals(array)
+
+Takes a `array` value.
+
+The array **must** be in the following format `[moment(),moment()]`
+
+For example:
+
+ disabledTimeIntervals: [[moment({ h: 0 }), moment({ h: 8 })], [moment({ h: 18 }), moment({ h: 24 })]]
+
+Will disable times between 12-8am and 6-12pm today
+
+----------------------
+
+### en/disabledHours
+
+<small>4.14.30</small> Issue: #851
+
+#### disabledHours()
+
+Returns an `array` variable with the currently set `options.en/disabledHours` option.
+
+#### disabledHours(boolean)
+
+Takes a `array` value.
+
+Must be in 24 hour format. Will allow or disallow hour selections (much like `disabledTimeIntervals`) but will affect all days.
+
+Like `en/disabledDates`, these options are mutually exclusive and will reset one of the options back to false.
+
+ disabledHours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23, 24]
+ enabledHours: [9, 10, 11, 12, 13, 14, 15, 16]
+
+----------------------
+
+### viewDate
+
+<small>4.14.30</small> Issue #872
+
+#### viewDate()
+
+Returns a `moment` variable with the currently set `options.viewDate` option.
+
+#### viewDate(viewDate)
+
+Takes a `string, moment or Date` value.
+
+This will change the `viewDate` without changing or setting the selected date.
+
+### parseInputDate
+<small>4.14.30</small> Issue #1095
+
+#### parseInputDate()
+
+Returns a `function` with the currently set `options.parseInputDate`
+
+#### parseInputDate(function)
+
+Takes a `function`
+
+Allows custom input formatting For example: the user can enter 'yesterday' or '30 days ago'.
+
+Example:
+
+```
+var parseRelativeDate = function(relativeDate) {
+
+ switch (relativeDate) {
+ case 'today':
+ return moment()
+ case 'yesterday':
+ return moment().subtract(1, 'day');
+ default:
+ return moment()
+ .subtract(Number(relativeDate.replace("days ago", "").trim()), 'days');
+ }
+}
+
+var parseInputDate = function(inputDate) {
+ var relativeDatePattern = /today|yesterday|[0-9]+\s+(days ago)/,
+ resultDate;
+
+ if (moment.isMoment(inputDate) || inputDate instanceof Date) {
+ resultDate = moment(inputDate);
+ } else {
+ var relativeDate = inputDate.match(relativeDatePattern),
+ parseDate = null;
+
+ if (relativeDate !== null)
+ parseDate = this.parseRelativeDate(inputDate.match(relativeDatePattern)[0]);
+ else
+ parseDate = moment();
+
+ resultDate = moment(parseDate, "YYYY-MM-DD");
+ }
+
+ return resultDate;
+}
+```
+
+----------------------
+
+### tooltips
+<small>4.14.30</small>
+
+#### tooltips()
+
+Returns an `Ojbect` of `options.tooltips`
+
+#### tooltips(tooltips)
+
+Takes an `Ojbect` of `strings`.
+
+##### Throws
+
+* `TypeError` - if tooltips parameter is not an `Ojbect`
\ No newline at end of file
diff --git a/docs/Installing.md b/docs/Installing.md
new file mode 100644
index 0000000..84fde42
--- /dev/null
+++ b/docs/Installing.md
@@ -0,0 +1,161 @@
+# Minimal Requirements
+
+1. jQuery
+2. Moment.js
+3. Bootstrap.js (transition and collapse are required if you're not using the full Bootstrap)
+4. Bootstrap Datepicker script
+5. Bootstrap CSS
+6. Bootstrap Datepicker CSS
+7. Locales: Moment's locale files are [here](https://github.com/moment/moment/tree/master/locale)
+
+# Installation Guides
+* [Bower](#bower-)
+* [Nuget](#nuget)
+* [Rails](#rails-)
+* [Angular](#angular-wrapper)
+* [Meteor.js](#meteorjs)
+* [Manual](#manual)
+
+## [bower](http://bower.io) ![Bower version](https://badge.fury.io/bo/eonasdan-bootstrap-datetimepicker.png)
+
+Run the following command:
+```
+bower install eonasdan-bootstrap-datetimepicker#latest --save
+```
+
+Include necessary scripts and styles:
+```html
+<head>
+ <!-- ... -->
+ <script type="text/javascript" src="/bower_components/jquery/jquery.min.js"></script>
+ <script type="text/javascript" src="/bower_components/moment/min/moment.min.js"></script>
+ <script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
+ <script type="text/javascript" src="/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js"></script>
+ <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css" />
+ <link rel="stylesheet" href="/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" />
+</head>
+```
+## Nuget
+### [LESS](https://www.nuget.org/packages/Bootstrap.v3.Datetimepicker/): ![NuGet version](https://badge.fury.io/nu/Bootstrap.v3.Datetimepicker.png)
+```
+PM> Install-Package Bootstrap.v3.Datetimepicker
+```
+
+### [CSS](https://www.nuget.org/packages/Bootstrap.v3.Datetimepicker.CSS/): ![NuGet version](https://badge.fury.io/nu/Bootstrap.v3.Datetimepicker.CSS.png)
+```
+PM> Install-Package Bootstrap.v3.Datetimepicker.CSS
+```
+
+```html
+<head>
+ <script type="text/javascript" src="/scripts/jquery.min.js"></script>
+ <script type="text/javascript" src="/scripts/moment.min.js"></script>
+ <script type="text/javascript" src="/scripts/bootstrap.min.js"></script>
+ <script type="text/javascript" src="/scripts/bootstrap-datetimepicker.*js"></script>
+ <!-- include your less or built css files -->
+ <!--
+ bootstrap-datetimepicker-build.less will pull in "../bootstrap/variables.less" and "bootstrap-datetimepicker.less";
+ or
+ <link rel="stylesheet" href="/Content/bootstrap-datetimepicker.css" />
+ -->
+</head>
+```
+
+## [Rails](http://rubygems.org/gems/bootstrap3-datetimepicker-rails) ![Gem Version](https://badge.fury.io/rb/bootstrap3-datetimepicker-rails.png)
+
+Add the following to your `Gemfile`:
+```ruby
+gem 'momentjs-rails', '>= 2.9.0'
+gem 'bootstrap3-datetimepicker-rails', '~> 4.14.30'
+```
+Note: You may need to change the version number above to the version number on the badge above.
+Read the rest of the install instructions @
+[TrevorS/bootstrap3-datetimepicker-rails](https://github.com/TrevorS/bootstrap3-datetimepicker-rails)
+
+
+## Angular Wrapper
+Follow the link [here](https://gist.github.com/eugenekgn/f00c4d764430642dca4b)
+
+## Meteor.js
+
+This widget has been package for the [Meteor.js](http://www.meteor.com/) platform, to install it use meteorite as follows:
+
+`$ mrt add tsega:bootstrap3-datetimepicker`
+
+For more detail see the package page on [Atmosphere](http://atmospherejs.com/package/bootstrap3-datetimepicker)
+
+## Manual
+
+### Acquire [jQuery](http://jquery.com)
+### Acquire [Moment.js](https://github.com/moment/moment)
+### Bootstrap 3 collapse and transition plugins
+Make sure to include *.JS files for plugins [collapse](http://getbootstrap.com/javascript/#collapse) and [transitions](http://getbootstrap.com/javascript/#transitions). They are included with [bootstrap in js/ directory](https://github.com/twbs/bootstrap/tree/master/js)
+Alternatively you could include the whole bundle of bootstrap plugins from [bootstrap.js](https://github.com/twbs/bootstrap/tree/master/dist/js)
+
+```html
+<script type="text/javascript" src="/path/to/jquery.js"></script>
+<script type="text/javascript" src="/path/to/moment.js"></script>
+<script type="text/javascript" src="/path/to/bootstrap/js/transition.js"></script>
+<script type="text/javascript" src="/path/to/bootstrap/js/collapse.js"></script>
+<script type="text/javascript" src="/path/to/bootstrap/dist/bootstrap.min.js"></script>
+<script type="text/javascript" src="/path/to/bootstrap-datetimepicker.min.js"></script>
+```
+
+## Knockout
+
+```
+ko.bindingHandlers.dateTimePicker = {
+ init: function (element, valueAccessor, allBindingsAccessor) {
+ //initialize datepicker with some optional options
+ var options = allBindingsAccessor().dateTimePickerOptions || {};
+ $(element).datetimepicker(options);
+
+ //when a user changes the date, update the view model
+ ko.utils.registerEventHandler(element, "dp.change", function (event) {
+ var value = valueAccessor();
+ if (ko.isObservable(value)) {
+ if (event.date != null && !(event.date instanceof Date)) {
+ value(event.date.toDate());
+ } else {
+ value(event.date);
+ }
+ }
+ });
+
+ ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
+ var picker = $(element).data("DateTimePicker");
+ if (picker) {
+ picker.destroy();
+ }
+ });
+ },
+ update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
+
+ var picker = $(element).data("DateTimePicker");
+ //when the view model is updated, update the widget
+ if (picker) {
+ var koDate = ko.utils.unwrapObservable(valueAccessor());
+
+ //in case return from server datetime i am get in this form for example /Date(93989393)/ then fomat this
+ koDate = (typeof (koDate) !== 'object') ? new Date(parseFloat(koDate.replace(/[^0-9]/g, ''))) : koDate;
+
+ picker.date(koDate);
+ }
+ }
+};
+```
+
+### CSS styles
+
+#### Using LESS
+```css
+ at import "/path/to/bootstrap/less/variables";
+ at import "/path/to/bootstrap-datetimepicker/src/less/bootstrap-datetimepicker-build.less";
+
+// [...] your custom styles and variables
+```
+
+Using CSS (default color palette)
+```html
+<link rel="stylesheet" href="/path/to/bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" />
+```
\ No newline at end of file
diff --git a/docs/Options.md b/docs/Options.md
new file mode 100644
index 0000000..993343c
--- /dev/null
+++ b/docs/Options.md
@@ -0,0 +1,473 @@
+## Options
+
+### format
+
+ Default: false
+
+See [momentjs' docs](http://momentjs.com/docs/#/displaying/format/) for valid formats. Format also dictates what components are shown, e.g. `MM/dd/YYYY` will not display the time picker.
+
+----------------------
+
+
+### dayViewHeaderFormat
+
+ Default: 'MMMM YYYY'
+
+Changes the heading of the datepicker when in "days" view.
+
+![Datepicker Header](img/dpheader.png)
+
+----------------------
+
+
+### extraFormats
+
+ Default: false
+
+Allows for several input formats to be valid. See [this PR](https://github.com/Eonasdan/bootstrap-datetimepicker/pull/666).
+
+----------------------
+
+
+### stepping
+
+ Default: 1
+
+Number of minutes the up/down arrow's will move the minutes value in the time picker
+
+----------------------
+
+
+### minDate
+
+ Default: false
+ Accepts: date, moment, string
+
+Prevents date/time selections before this date.
+
+#### `minDate` will override `defaultDate` and `useCurrent` if either of these settings are the same day since both options are invalid according to the rules you've selected.
+
+----------------------
+
+
+### maxDate
+
+ Default: false
+ Accepts: date, moment, string
+
+Prevents date/time selections after this date.
+
+#### `maxDate` will override `defaultDate` and `useCurrent` if either of these settings are the same day since both options are invalid according to the rules you've selected.
+
+----------------------
+
+
+### useCurrent
+
+ Default: true
+
+On show, will set the picker to the current date/time
+
+----------------------
+
+
+### collapse
+
+ Default: true
+
+Using a Bootstraps collapse to switch between date/time pickers.
+
+----------------------
+
+
+### locale
+
+ Default: moment.locale()
+ Accepts: string, moment.local('locale')
+
+See [momentjs](https://github.com/moment/moment/tree/develop/locale) for valid locales.
+
+You must include `moment-with-locales.js` or a local js file.
+
+----------------------
+
+
+### defaultDate
+
+ Default: false
+ Accepts: date, moment, string
+
+Sets the picker default date/time. Overrides `useCurrent`
+
+----------------------
+
+
+### disabledDates
+
+ Default: false
+ Accepts: array of [date, moment, string]
+
+Disables selection of dates in the array, e.g. holidays
+
+----------------------
+
+
+### enabledDates
+
+ Default: false
+ Accepts: array of [date, moment, string]
+
+Disables selection of dates **NOT** in the array, e.g. holidays
+
+----------------------
+
+
+### icons
+
+ Default: {
+ time: 'glyphicon glyphicon-time',
+ date: 'glyphicon glyphicon-calendar',
+ up: 'glyphicon glyphicon-chevron-up',
+ down: 'glyphicon glyphicon-chevron-down',
+ previous: 'glyphicon glyphicon-chevron-left',
+ next: 'glyphicon glyphicon-chevron-right',
+ today: 'glyphicon glyphicon-screenshot',
+ clear: 'glyphicon glyphicon-trash',
+ close: 'glyphicon glyphicon-remove'
+ }
+ Accepts: object with all or some of the parameters above
+
+Change the default icons for the pickers functions.
+
+### useStrict
+
+ Default: false
+
+Defines if moment should use strict date parsing when considering a date to be valid
+
+----------------------
+
+
+### sideBySide
+
+ Default: false
+
+Shows the picker side by side when using the time and date together.
+
+![SideBySide](img/sideBySide.png)
+
+----------------------
+
+
+### daysOfWeekDisabled
+
+ Default: []
+ Accepts: array of numbers from 0-6
+
+Disables the section of days of the week, e.g. weekends.
+
+----------------------
+
+
+### calendarWeeks
+
+ Default: false
+
+Shows the week of the year to the left of first day of the week.
+
+![calendarWeek](img/calendarWeeks.png)
+
+----------------------
+
+
+### viewMode
+
+ Default: 'days'
+ Accepts: 'decades','years','months','days'
+
+The default view to display when the picker is shown.
+
+**Note**: To limit the picker to selecting, for instance the year and month, use `format: MM/YYYY`
+
+----------------------
+
+
+### toolbarPlacement
+
+ Default: 'default'
+ Accepts: 'default', 'top', 'bottom'
+
+Changes the placement of the icon toolbar.
+
+![toolbarPlacement](img/toolbarPlacement.png)
+
+----------------------
+
+
+### showTodayButton
+
+ Default: false
+
+Show the "Today" button in the icon toolbar.
+
+Clicking the "Today" button will set the calendar view and set the date to `now`.
+
+----------------------
+
+
+### showClear
+
+ Default: false
+
+Show the "Clear" button in the icon toolbar.
+
+Clicking the "Clear" button will set the calendar to null.
+
+----------------------
+
+### showClose
+
+ Default: false
+
+Show the "Close" button in the icon toolbar.
+
+Clicking the "Close" button will call `hide()`
+
+----------------------
+
+
+### widgetPositioning
+
+ Default: {
+ horizontal: 'auto'
+ vertical: 'auto'
+ }
+ Accepts: object with the all or one of the parameters above
+ horizontal: 'auto', 'left', 'right'
+ vertical: 'auto', 'top', 'bottom'
+
+### widgetParent
+
+ Default: null
+ Accepts: string or jQuery object
+
+On picker show, places the widget at the identifier (string) or jQuery object **if** the element has css `position: 'relative'`
+
+----------------------
+
+
+### keepOpen
+
+ Default: false
+
+Will cause the date picker to stay open after selecting a date if no time components are being used.
+
+----------------------
+
+
+### inline
+
+ Default: false
+
+Will display the picker inline without the need of a input field. This will also hide borders and shadows.
+
+----------------------
+
+
+### keepInvalid
+
+ Default: false
+
+Will cause the date picker to **not** revert or overwrite invalid dates.
+
+----------------------
+
+
+### keyBinds
+
+ Default: up: function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().subtract(7, 'd'));
+ } else {
+ this.date(this.date().clone().add(1, 'm'));
+ }
+ },
+ down: function (widget) {
+ if (!widget) {
+ this.show();
+ }
+ else if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().add(7, 'd'));
+ } else {
+ this.date(this.date().clone().subtract(1, 'm'));
+ }
+ },
+ 'control up': function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().subtract(1, 'y'));
+ } else {
+ this.date(this.date().clone().add(1, 'h'));
+ }
+ },
+ 'control down': function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().add(1, 'y'));
+ } else {
+ this.date(this.date().clone().subtract(1, 'h'));
+ }
+ },
+ left: function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().subtract(1, 'd'));
+ }
+ },
+ right: function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().add(1, 'd'));
+ }
+ },
+ pageUp: function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().subtract(1, 'M'));
+ }
+ },
+ pageDown: function (widget) {
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(this.date().clone().add(1, 'M'));
+ }
+ },
+ enter: function () {
+ this.hide();
+ },
+ escape: function () {
+ this.hide();
+ },
+ 'control space': function (widget) {
+ if (widget.find('.timepicker').is(':visible')) {
+ widget.find('.btn[data-action="togglePeriod"]').click();
+ }
+ },
+ t: function () {
+ this.date(moment());
+ },
+ 'delete': function () {
+ this.clear();
+ }
+
+Allows for custom events to fire on keyboard press.
+
+----------------------
+
+
+### debug
+
+<small>4.7.14</small>
+
+ Default: false
+
+Will cause the date picker to stay open after a `blur` event.
+
+----------------------
+
+
+### ignoreReadonly
+
+<small>4.7.14</small>
+
+ Default: false
+
+Allow date picker show event to fire even when the associated input element has the `readonly="readonly"`property.
+
+----------------------
+
+
+### disabledTimeIntervals
+
+<small>4.14.30</small>
+
+ Default: false
+
+Disables time selection between the given `moments`
+
+----------------------
+
+
+### allowInputToggle
+
+<small>4.14.30</small>
+
+ Default: false
+
+If `true`, the picker will show on textbox focus and icon click when used in a button group
+
+----------------------
+
+
+### focusOnShow
+
+<small>4.14.30</small>
+
+ Default: true
+
+If `false`, the textbox will not be given focus when the picker is shown
+
+----------------------
+
+
+### enabledHours
+
+<small>4.14.30</small>
+
+ Default: false
+
+Will allow or disallow hour selections (much like `disabledTimeIntervals`) but will affect all days
+
+----------------------
+
+
+### disabledHours
+
+<small>4.14.30</small>
+
+ Default: false
+
+Will allow or disallow hour selections (much like `disabledTimeIntervals`) but will affect all days
+
+----------------------
+
+
+### viewDate
+
+<small>4.14.30</small>
+
+ Default: false
+
+This will change the `viewDate` without changing or setting the selected date.
+
+----------------------
+
+
+### tooltips
+
+<small>4.15.35</small>
+
+```
+tooltips: {
+ today: 'Go to today',
+ clear: 'Clear selection',
+ close: 'Close the picker',
+ selectMonth: 'Select Month',
+ prevMonth: 'Previous Month',
+ nextMonth: 'Next Month',
+ selectYear: 'Select Year',
+ prevYear: 'Previous Year',
+ nextYear: 'Next Year',
+ selectDecade: 'Select Decade',
+ prevDecade: 'Previous Decade',
+ nextDecade: 'Next Decade',
+ prevCentury: 'Previous Century',
+ nextCentury: 'Next Century'
+}
+```
+
+This will change the `tooltips` over each icon to a custom string
diff --git a/docs/Version 4 Changelog.md b/docs/Version 4 Changelog.md
new file mode 100644
index 0000000..f3c30de
--- /dev/null
+++ b/docs/Version 4 Changelog.md
@@ -0,0 +1,6 @@
+<meta http-equiv="refresh" content="1; url=/Changelog/"/>
+<meta http-equiv="refresh" content="0; url=/Changelog/"/>
+<link rel="canonical" href="/Changelog/">
+
+ <p>The page has moved to:
+ <a href="/Changelog/">this page</a></p>
\ No newline at end of file
diff --git a/docs/Version 4 Contributors guide.md b/docs/Version 4 Contributors guide.md
new file mode 100644
index 0000000..0378e0f
--- /dev/null
+++ b/docs/Version 4 Contributors guide.md
@@ -0,0 +1,6 @@
+<meta http-equiv="refresh" content="1; url=/Changelog/"/>
+<meta http-equiv="refresh" content="0; url=/ContributorsGuide/"/>
+<link rel="canonical" href="/ContributorsGuide/">
+
+ <p>The page has moved to:
+ <a href="/ContributorsGuide/">this page</a></p>
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..d60df0b
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,592 @@
+#Bootstrap 3 Datepicker v4 Docs
+
+<div class="alert alert-info">
+ <strong>Note</strong>
+ All functions are accessed via the <code>data</code> attribute e.g. <code>$('#datetimepicker').data("DateTimePicker").FUNCTION()</code>
+</div>
+
+### Minimum Setup
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker1'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker1').datetimepicker();
+ });
+ </script>
+ </div>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker1'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker1').datetimepicker();
+ });
+ </script>
+ </div>
+</div>
+```
+
+----------------------
+
+### Using Locales
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker2'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker2').datetimepicker({
+ locale: 'ru'
+ });
+ });
+ </script>
+ </div>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker2'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker2').datetimepicker({
+ locale: 'ru'
+ });
+ });
+ </script>
+ </div>
+</div>
+```
+
+----------------------
+
+### Custom Formats
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker3'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-time"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker3').datetimepicker({
+ format: 'LT'
+ });
+ });
+ </script>
+ </div>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker3'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-time"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker3').datetimepicker({
+ format: 'LT'
+ });
+ });
+ </script>
+ </div>
+</div>
+```
+
+----------------------
+
+### No Icon (input field only):
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <input type='text' class="form-control" id='datetimepicker4' />
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker4').datetimepicker();
+ });
+ </script>
+ </div>
+</div>
+
+#### Code
+
+```
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <input type='text' class="form-control" id='datetimepicker4' />
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker4').datetimepicker();
+ });
+ </script>
+ </div>
+</div>
+```
+
+----------------------
+
+### Enabled/Disabled Dates
+
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker5'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker5').datetimepicker({
+ defaultDate: "11/1/2013",
+ disabledDates: [
+ moment("12/25/2013"),
+ new Date(2013, 11 - 1, 21),
+ "11/22/2013 00:53"
+ ]
+ });
+ });
+ </script>
+ </div>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="row">
+ <div class='col-sm-6'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker5'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker5').datetimepicker({
+ defaultDate: "11/1/2013",
+ disabledDates: [
+ moment("12/25/2013"),
+ new Date(2013, 11 - 1, 21),
+ "11/22/2013 00:53"
+ ]
+ });
+ });
+ </script>
+ </div>
+</div>
+```
+
+----------------------
+
+### Linked Pickers
+
+<div class="container">
+ <div class='col-md-5'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker6'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class='col-md-5'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker7'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+</div>
+<script type="text/javascript">
+ $(function () {
+ $('#datetimepicker6').datetimepicker();
+ $('#datetimepicker7').datetimepicker({
+ useCurrent: false
+ });
+ $("#datetimepicker6").on("dp.change", function (e) {
+ $('#datetimepicker7').data("DateTimePicker").minDate(e.date);
+ });
+ $("#datetimepicker7").on("dp.change", function (e) {
+ $('#datetimepicker6').data("DateTimePicker").maxDate(e.date);
+ });
+ });
+</script>
+
+#### Code
+
+```
+<div class="container">
+ <div class='col-md-5'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker6'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class='col-md-5'>
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker7'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ </div>
+ </div>
+ </div>
+</div>
+<script type="text/javascript">
+ $(function () {
+ $('#datetimepicker6').datetimepicker();
+ $('#datetimepicker7').datetimepicker({
+ useCurrent: false //Important! See issue #1075
+ });
+ $("#datetimepicker6").on("dp.change", function (e) {
+ $('#datetimepicker7').data("DateTimePicker").minDate(e.date);
+ });
+ $("#datetimepicker7").on("dp.change", function (e) {
+ $('#datetimepicker6').data("DateTimePicker").maxDate(e.date);
+ });
+ });
+</script>
+```
+
+----------------------
+
+### Custom Icons
+
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker8'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="fa fa-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker8').datetimepicker({
+ icons: {
+ time: "fa fa-clock-o",
+ date: "fa fa-calendar",
+ up: "fa fa-arrow-up",
+ down: "fa fa-arrow-down"
+ }
+ });
+ });
+ </script>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker8'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="fa fa-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker8').datetimepicker({
+ icons: {
+ time: "fa fa-clock-o",
+ date: "fa fa-calendar",
+ up: "fa fa-arrow-up",
+ down: "fa fa-arrow-down"
+ }
+ });
+ });
+ </script>
+</div>
+```
+
+----------------------
+
+### View Mode
+
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker9'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker9').datetimepicker({
+ viewMode: 'years'
+ });
+ });
+ </script>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker9'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker9').datetimepicker({
+ viewMode: 'years'
+ });
+ });
+ </script>
+</div>
+```
+
+----------------------
+
+### Min View Mode
+
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker10'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker10').datetimepicker({
+ viewMode: 'years',
+ format: 'MM/YYYY'
+ });
+ });
+ </script>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker10'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker10').datetimepicker({
+ viewMode: 'years',
+ format: 'MM/YYYY'
+ });
+ });
+ </script>
+</div>
+
+```
+
+----------------------
+
+### Disabled Days of the Week
+
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker11'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker11').datetimepicker({
+ daysOfWeekDisabled: [0, 6]
+ });
+ });
+ </script>
+</div>
+
+#### Code
+
+```
+<div class="container">
+ <div class="col-sm-6" style="height:130px;">
+ <div class="form-group">
+ <div class='input-group date' id='datetimepicker11'>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar">
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker11').datetimepicker({
+ daysOfWeekDisabled: [0, 6]
+ });
+ });
+ </script>
+</div>
+```
+
+----------------------
+
+### Inline
+
+<div style="overflow:hidden;">
+ <div class="form-group">
+ <div class="row">
+ <div class="col-md-8">
+ <div id="datetimepicker12"></div>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker12').datetimepicker({
+ inline: true,
+ sideBySide: true
+ });
+ });
+ </script>
+</div>
+
+#### Code
+
+```
+<div style="overflow:hidden;">
+ <div class="form-group">
+ <div class="row">
+ <div class="col-md-8">
+ <div id="datetimepicker12"></div>
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ $(function () {
+ $('#datetimepicker12').datetimepicker({
+ inline: true,
+ sideBySide: true
+ });
+ });
+ </script>
+</div>
+```
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..5df9623
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,17 @@
+site_name: Bootstrap 3 Datepicker
+theme: bootstrap
+extra_javascript: ['//cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment-with-locales.js','//cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/e8bddc60e73c1ec2475f827be36e1957af72e2ea/src/js/bootstrap-datetimepicker.js']
+extra_css: ['//cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/e8bddc60e73c1ec2475f827be36e1957af72e2ea/build/css/bootstrap-datetimepicker.css']
+repo_url: https://github.com/Eonasdan/bootstrap-datetimepicker
+pages:
+- ['index.md', 'Usage']
+- ['Installing.md', 'Installing']
+- ['Functions.md', 'Functions']
+- ['Options.md', 'Options']
+- ['Events.md', 'Events']
+- ['Changelog.md', 'Change Log']
+- ['ContributorsGuide.md', 'Dev Guide']
+- ['Extras.md', 'Extras']
+- ['FAQ.md', 'FAQs']
+- ['Version 4 Changelog.md', '']
+- ['Version 4 Contributors guide.md', '']
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..01f82d1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,43 @@
+{
+ "author": {
+ "name": "Jonathan Peterson"
+ },
+ "bugs": {
+ "url": "https://github.com/eonasdan/bootstrap-datetimepicker/issues"
+ },
+ "dependencies": {
+ "moment": "~2.8",
+ "moment-timezone" : "~0.4",
+ "bootstrap": "^3.3",
+ "jquery": ">=1.8.3 <2.2.0"
+ },
+ "description": "A date/time picker component designed to work with Bootstrap 3 and Momentjs. For usage, installation and demos see Project Site on GitHub",
+ "devDependencies": {
+ "grunt": "latest",
+ "grunt-contrib-jasmine": "^0.7.0",
+ "grunt-contrib-jshint": "latest",
+ "grunt-contrib-less": "latest",
+ "grunt-contrib-uglify": "latest",
+ "grunt-jscs": "latest",
+ "grunt-string-replace": "latest",
+ "load-grunt-tasks": "latest",
+ "grunt-nuget": "^0.1.4"
+ },
+ "homepage": "http://eonasdan.github.io/bootstrap-datetimepicker/",
+ "keywords": [
+ "twitter-bootstrap",
+ "bootstrap",
+ "datepicker",
+ "datetimepicker",
+ "timepicker",
+ "moment"
+ ],
+ "license": "MIT",
+ "main": "src/js/bootstrap-datetimepicker.js",
+ "name": "eonasdan-bootstrap-datetimepicker",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/eonasdan/bootstrap-datetimepicker.git"
+ },
+ "version": "4.17.37"
+}
diff --git a/src/js/bootstrap-datetimepicker.js b/src/js/bootstrap-datetimepicker.js
new file mode 100644
index 0000000..31e4e6a
--- /dev/null
+++ b/src/js/bootstrap-datetimepicker.js
@@ -0,0 +1,2552 @@
+/*! version : 4.17.37
+ =========================================================
+ bootstrap-datetimejs
+ https://github.com/Eonasdan/bootstrap-datetimepicker
+ Copyright (c) 2015 Jonathan Peterson
+ =========================================================
+ */
+/*
+ The MIT License (MIT)
+
+ Copyright (c) 2015 Jonathan Peterson
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+/*global define:false */
+/*global exports:false */
+/*global require:false */
+/*global jQuery:false */
+/*global moment:false */
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // AMD is used - Register as an anonymous module.
+ define(['jquery', 'moment'], factory);
+ } else if (typeof exports === 'object') {
+ factory(require('jquery'), require('moment'));
+ } else {
+ // Neither AMD nor CommonJS used. Use global variables.
+ if (typeof jQuery === 'undefined') {
+ throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
+ }
+ if (typeof moment === 'undefined') {
+ throw 'bootstrap-datetimepicker requires Moment.js to be loaded first';
+ }
+ factory(jQuery, moment);
+ }
+}(function ($, moment) {
+ 'use strict';
+ if (!moment) {
+ throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first');
+ }
+
+ var dateTimePicker = function (element, options) {
+ var picker = {},
+ date,
+ viewDate,
+ unset = true,
+ input,
+ component = false,
+ widget = false,
+ use24Hours,
+ minViewModeNumber = 0,
+ actualFormat,
+ parseFormats,
+ currentViewMode,
+ datePickerModes = [
+ {
+ clsName: 'days',
+ navFnc: 'M',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'y',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'y',
+ navStep: 10
+ },
+ {
+ clsName: 'decades',
+ navFnc: 'y',
+ navStep: 100
+ }
+ ],
+ viewModes = ['days', 'months', 'years', 'decades'],
+ verticalModes = ['top', 'bottom', 'auto'],
+ horizontalModes = ['left', 'right', 'auto'],
+ toolbarPlacements = ['default', 'top', 'bottom'],
+ keyMap = {
+ 'up': 38,
+ 38: 'up',
+ 'down': 40,
+ 40: 'down',
+ 'left': 37,
+ 37: 'left',
+ 'right': 39,
+ 39: 'right',
+ 'tab': 9,
+ 9: 'tab',
+ 'escape': 27,
+ 27: 'escape',
+ 'enter': 13,
+ 13: 'enter',
+ 'pageUp': 33,
+ 33: 'pageUp',
+ 'pageDown': 34,
+ 34: 'pageDown',
+ 'shift': 16,
+ 16: 'shift',
+ 'control': 17,
+ 17: 'control',
+ 'space': 32,
+ 32: 'space',
+ 't': 84,
+ 84: 't',
+ 'delete': 46,
+ 46: 'delete'
+ },
+ keyState = {},
+
+ /********************************************************************************
+ *
+ * Private functions
+ *
+ ********************************************************************************/
+ getMoment = function (d) {
+ var tzEnabled = false,
+ returnMoment,
+ currentZoneOffset,
+ incomingZoneOffset,
+ timeZoneIndicator,
+ dateWithTimeZoneInfo;
+
+ if (moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== '') {
+ tzEnabled = true;
+ }
+ if (d === undefined || d === null) {
+ if (tzEnabled) {
+ returnMoment = moment().tz(options.timeZone).startOf('d');
+ } else {
+ returnMoment = moment().startOf('d');
+ }
+ } else {
+ if (tzEnabled) {
+ currentZoneOffset = moment().tz(options.timeZone).utcOffset();
+ incomingZoneOffset = moment(d, parseFormats, options.useStrict).utcOffset();
+ if (incomingZoneOffset !== currentZoneOffset) {
+ timeZoneIndicator = moment().tz(options.timeZone).format('Z');
+ dateWithTimeZoneInfo = moment(d, parseFormats, options.useStrict).format('YYYY-MM-DD[T]HH:mm:ss') + timeZoneIndicator;
+ returnMoment = moment(dateWithTimeZoneInfo, parseFormats, options.useStrict).tz(options.timeZone);
+ } else {
+ returnMoment = moment(d, parseFormats, options.useStrict).tz(options.timeZone);
+ }
+ } else {
+ returnMoment = moment(d, parseFormats, options.useStrict);
+ }
+ }
+ return returnMoment;
+ },
+ isEnabled = function (granularity) {
+ if (typeof granularity !== 'string' || granularity.length > 1) {
+ throw new TypeError('isEnabled expects a single character string parameter');
+ }
+ switch (granularity) {
+ case 'y':
+ return actualFormat.indexOf('Y') !== -1;
+ case 'M':
+ return actualFormat.indexOf('M') !== -1;
+ case 'd':
+ return actualFormat.toLowerCase().indexOf('d') !== -1;
+ case 'h':
+ case 'H':
+ return actualFormat.toLowerCase().indexOf('h') !== -1;
+ case 'm':
+ return actualFormat.indexOf('m') !== -1;
+ case 's':
+ return actualFormat.indexOf('s') !== -1;
+ default:
+ return false;
+ }
+ },
+ hasTime = function () {
+ return (isEnabled('h') || isEnabled('m') || isEnabled('s'));
+ },
+
+ hasDate = function () {
+ return (isEnabled('y') || isEnabled('M') || isEnabled('d'));
+ },
+
+ getDatePickerTemplate = function () {
+ var headTemplate = $('<thead>')
+ .append($('<tr>')
+ .append($('<th>').addClass('prev').attr('data-action', 'previous')
+ .append($('<span>').addClass(options.icons.previous))
+ )
+ .append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5')))
+ .append($('<th>').addClass('next').attr('data-action', 'next')
+ .append($('<span>').addClass(options.icons.next))
+ )
+ ),
+ contTemplate = $('<tbody>')
+ .append($('<tr>')
+ .append($('<td>').attr('colspan', (options.calendarWeeks ? '8' : '7')))
+ );
+
+ return [
+ $('<div>').addClass('datepicker-days')
+ .append($('<table>').addClass('table-condensed')
+ .append(headTemplate)
+ .append($('<tbody>'))
+ ),
+ $('<div>').addClass('datepicker-months')
+ .append($('<table>').addClass('table-condensed')
+ .append(headTemplate.clone())
+ .append(contTemplate.clone())
+ ),
+ $('<div>').addClass('datepicker-years')
+ .append($('<table>').addClass('table-condensed')
+ .append(headTemplate.clone())
+ .append(contTemplate.clone())
+ ),
+ $('<div>').addClass('datepicker-decades')
+ .append($('<table>').addClass('table-condensed')
+ .append(headTemplate.clone())
+ .append(contTemplate.clone())
+ )
+ ];
+ },
+
+ getTimePickerMainTemplate = function () {
+ var topRow = $('<tr>'),
+ middleRow = $('<tr>'),
+ bottomRow = $('<tr>');
+
+ if (isEnabled('h')) {
+ topRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour}).addClass('btn').attr('data-action', 'incrementHours')
+ .append($('<span>').addClass(options.icons.up))));
+ middleRow.append($('<td>')
+ .append($('<span>').addClass('timepicker-hour').attr({'data-time-component':'hours', 'title': options.tooltips.pickHour}).attr('data-action', 'showHours')));
+ bottomRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour}).addClass('btn').attr('data-action', 'decrementHours')
+ .append($('<span>').addClass(options.icons.down))));
+ }
+ if (isEnabled('m')) {
+ if (isEnabled('h')) {
+ topRow.append($('<td>').addClass('separator'));
+ middleRow.append($('<td>').addClass('separator').html(':'));
+ bottomRow.append($('<td>').addClass('separator'));
+ }
+ topRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute}).addClass('btn').attr('data-action', 'incrementMinutes')
+ .append($('<span>').addClass(options.icons.up))));
+ middleRow.append($('<td>')
+ .append($('<span>').addClass('timepicker-minute').attr({'data-time-component': 'minutes', 'title': options.tooltips.pickMinute}).attr('data-action', 'showMinutes')));
+ bottomRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute}).addClass('btn').attr('data-action', 'decrementMinutes')
+ .append($('<span>').addClass(options.icons.down))));
+ }
+ if (isEnabled('s')) {
+ if (isEnabled('m')) {
+ topRow.append($('<td>').addClass('separator'));
+ middleRow.append($('<td>').addClass('separator').html(':'));
+ bottomRow.append($('<td>').addClass('separator'));
+ }
+ topRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond}).addClass('btn').attr('data-action', 'incrementSeconds')
+ .append($('<span>').addClass(options.icons.up))));
+ middleRow.append($('<td>')
+ .append($('<span>').addClass('timepicker-second').attr({'data-time-component': 'seconds', 'title': options.tooltips.pickSecond}).attr('data-action', 'showSeconds')));
+ bottomRow.append($('<td>')
+ .append($('<a>').attr({href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond}).addClass('btn').attr('data-action', 'decrementSeconds')
+ .append($('<span>').addClass(options.icons.down))));
+ }
+
+ if (!use24Hours) {
+ topRow.append($('<td>').addClass('separator'));
+ middleRow.append($('<td>')
+ .append($('<button>').addClass('btn btn-primary').attr({'data-action': 'togglePeriod', tabindex: '-1', 'title': options.tooltips.togglePeriod})));
+ bottomRow.append($('<td>').addClass('separator'));
+ }
+
+ return $('<div>').addClass('timepicker-picker')
+ .append($('<table>').addClass('table-condensed')
+ .append([topRow, middleRow, bottomRow]));
+ },
+
+ getTimePickerTemplate = function () {
+ var hoursView = $('<div>').addClass('timepicker-hours')
+ .append($('<table>').addClass('table-condensed')),
+ minutesView = $('<div>').addClass('timepicker-minutes')
+ .append($('<table>').addClass('table-condensed')),
+ secondsView = $('<div>').addClass('timepicker-seconds')
+ .append($('<table>').addClass('table-condensed')),
+ ret = [getTimePickerMainTemplate()];
+
+ if (isEnabled('h')) {
+ ret.push(hoursView);
+ }
+ if (isEnabled('m')) {
+ ret.push(minutesView);
+ }
+ if (isEnabled('s')) {
+ ret.push(secondsView);
+ }
+
+ return ret;
+ },
+
+ getToolbar = function () {
+ var row = [];
+ if (options.showTodayButton) {
+ row.push($('<td>').append($('<a>').attr({'data-action':'today', 'title': options.tooltips.today}).append($('<span>').addClass(options.icons.today))));
+ }
+ if (!options.sideBySide && hasDate() && hasTime()) {
+ row.push($('<td>').append($('<a>').attr({'data-action':'togglePicker', 'title': options.tooltips.selectTime}).append($('<span>').addClass(options.icons.time))));
+ }
+ if (options.showClear) {
+ row.push($('<td>').append($('<a>').attr({'data-action':'clear', 'title': options.tooltips.clear}).append($('<span>').addClass(options.icons.clear))));
+ }
+ if (options.showClose) {
+ row.push($('<td>').append($('<a>').attr({'data-action':'close', 'title': options.tooltips.close}).append($('<span>').addClass(options.icons.close))));
+ }
+ return $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row)));
+ },
+
+ getTemplate = function () {
+ var template = $('<div>').addClass('bootstrap-datetimepicker-widget dropdown-menu'),
+ dateView = $('<div>').addClass('datepicker').append(getDatePickerTemplate()),
+ timeView = $('<div>').addClass('timepicker').append(getTimePickerTemplate()),
+ content = $('<ul>').addClass('list-unstyled'),
+ toolbar = $('<li>').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar());
+
+ if (options.inline) {
+ template.removeClass('dropdown-menu');
+ }
+
+ if (use24Hours) {
+ template.addClass('usetwentyfour');
+ }
+ if (isEnabled('s') && !use24Hours) {
+ template.addClass('wider');
+ }
+
+ if (options.sideBySide && hasDate() && hasTime()) {
+ template.addClass('timepicker-sbs');
+ if (options.toolbarPlacement === 'top') {
+ template.append(toolbar);
+ }
+ template.append(
+ $('<div>').addClass('row')
+ .append(dateView.addClass('col-md-6'))
+ .append(timeView.addClass('col-md-6'))
+ );
+ if (options.toolbarPlacement === 'bottom') {
+ template.append(toolbar);
+ }
+ return template;
+ }
+
+ if (options.toolbarPlacement === 'top') {
+ content.append(toolbar);
+ }
+ if (hasDate()) {
+ content.append($('<li>').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView));
+ }
+ if (options.toolbarPlacement === 'default') {
+ content.append(toolbar);
+ }
+ if (hasTime()) {
+ content.append($('<li>').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView));
+ }
+ if (options.toolbarPlacement === 'bottom') {
+ content.append(toolbar);
+ }
+ return template.append(content);
+ },
+
+ dataToOptions = function () {
+ var eData,
+ dataOptions = {};
+
+ if (element.is('input') || options.inline) {
+ eData = element.data();
+ } else {
+ eData = element.find('input').data();
+ }
+
+ if (eData.dateOptions && eData.dateOptions instanceof Object) {
+ dataOptions = $.extend(true, dataOptions, eData.dateOptions);
+ }
+
+ $.each(options, function (key) {
+ var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1);
+ if (eData[attributeName] !== undefined) {
+ dataOptions[key] = eData[attributeName];
+ }
+ });
+ return dataOptions;
+ },
+
+ place = function () {
+ var position = (component || element).position(),
+ offset = (component || element).offset(),
+ vertical = options.widgetPositioning.vertical,
+ horizontal = options.widgetPositioning.horizontal,
+ parent;
+
+ if (options.widgetParent) {
+ parent = options.widgetParent.append(widget);
+ } else if (element.is('input')) {
+ parent = element.after(widget).parent();
+ } else if (options.inline) {
+ parent = element.append(widget);
+ return;
+ } else {
+ parent = element;
+ element.children().first().after(widget);
+ }
+
+ // Top and bottom logic
+ if (vertical === 'auto') {
+ if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() &&
+ widget.height() + element.outerHeight() < offset.top) {
+ vertical = 'top';
+ } else {
+ vertical = 'bottom';
+ }
+ }
+
+ // Left and right logic
+ if (horizontal === 'auto') {
+ if (parent.width() < offset.left + widget.outerWidth() / 2 &&
+ offset.left + widget.outerWidth() > $(window).width()) {
+ horizontal = 'right';
+ } else {
+ horizontal = 'left';
+ }
+ }
+
+ if (vertical === 'top') {
+ widget.addClass('top').removeClass('bottom');
+ } else {
+ widget.addClass('bottom').removeClass('top');
+ }
+
+ if (horizontal === 'right') {
+ widget.addClass('pull-right');
+ } else {
+ widget.removeClass('pull-right');
+ }
+
+ // find the first parent element that has a relative css positioning
+ if (parent.css('position') !== 'relative') {
+ parent = parent.parents().filter(function () {
+ return $(this).css('position') === 'relative';
+ }).first();
+ }
+
+ if (parent.length === 0) {
+ throw new Error('datetimepicker component should be placed within a relative positioned container');
+ }
+
+ widget.css({
+ top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(),
+ bottom: vertical === 'top' ? position.top + element.outerHeight() : 'auto',
+ left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto',
+ right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left)
+ });
+ },
+
+ notifyEvent = function (e) {
+ if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) {
+ return;
+ }
+ element.trigger(e);
+ },
+
+ viewUpdate = function (e) {
+ if (e === 'y') {
+ e = 'YYYY';
+ }
+ notifyEvent({
+ type: 'dp.update',
+ change: e,
+ viewDate: viewDate.clone()
+ });
+ },
+
+ showMode = function (dir) {
+ if (!widget) {
+ return;
+ }
+ if (dir) {
+ currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir));
+ }
+ widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show();
+ },
+
+ fillDow = function () {
+ var row = $('<tr>'),
+ currentDate = viewDate.clone().startOf('w').startOf('d');
+
+ if (options.calendarWeeks === true) {
+ row.append($('<th>').addClass('cw').text('#'));
+ }
+
+ while (currentDate.isBefore(viewDate.clone().endOf('w'))) {
+ row.append($('<th>').addClass('dow').text(currentDate.format('dd')));
+ currentDate.add(1, 'd');
+ }
+ widget.find('.datepicker-days thead').append(row);
+ },
+
+ isInDisabledDates = function (testDate) {
+ return options.disabledDates[testDate.format('YYYY-MM-DD')] === true;
+ },
+
+ isInEnabledDates = function (testDate) {
+ return options.enabledDates[testDate.format('YYYY-MM-DD')] === true;
+ },
+
+ isInDisabledHours = function (testDate) {
+ return options.disabledHours[testDate.format('H')] === true;
+ },
+
+ isInEnabledHours = function (testDate) {
+ return options.enabledHours[testDate.format('H')] === true;
+ },
+
+ isValid = function (targetMoment, granularity) {
+ if (!targetMoment.isValid()) {
+ return false;
+ }
+ if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) {
+ return false;
+ }
+ if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) {
+ return false;
+ }
+ if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) {
+ return false;
+ }
+ if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) {
+ return false;
+ }
+ if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) {
+ return false;
+ }
+ if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) {
+ return false;
+ }
+ if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) {
+ return false;
+ }
+ if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) {
+ var found = false;
+ $.each(options.disabledTimeIntervals, function () {
+ if (targetMoment.isBetween(this[0], this[1])) {
+ found = true;
+ return false;
+ }
+ });
+ if (found) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ fillMonths = function () {
+ var spans = [],
+ monthsShort = viewDate.clone().startOf('y').startOf('d');
+ while (monthsShort.isSame(viewDate, 'y')) {
+ spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM')));
+ monthsShort.add(1, 'M');
+ }
+ widget.find('.datepicker-months td').empty().append(spans);
+ },
+
+ updateMonths = function () {
+ var monthsView = widget.find('.datepicker-months'),
+ monthsViewHeader = monthsView.find('th'),
+ months = monthsView.find('tbody').find('span');
+
+ monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear);
+ monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear);
+ monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear);
+
+ monthsView.find('.disabled').removeClass('disabled');
+
+ if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) {
+ monthsViewHeader.eq(0).addClass('disabled');
+ }
+
+ monthsViewHeader.eq(1).text(viewDate.year());
+
+ if (!isValid(viewDate.clone().add(1, 'y'), 'y')) {
+ monthsViewHeader.eq(2).addClass('disabled');
+ }
+
+ months.removeClass('active');
+ if (date.isSame(viewDate, 'y') && !unset) {
+ months.eq(date.month()).addClass('active');
+ }
+
+ months.each(function (index) {
+ if (!isValid(viewDate.clone().month(index), 'M')) {
+ $(this).addClass('disabled');
+ }
+ });
+ },
+
+ updateYears = function () {
+ var yearsView = widget.find('.datepicker-years'),
+ yearsViewHeader = yearsView.find('th'),
+ startYear = viewDate.clone().subtract(5, 'y'),
+ endYear = viewDate.clone().add(6, 'y'),
+ html = '';
+
+ yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade);
+ yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade);
+ yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade);
+
+ yearsView.find('.disabled').removeClass('disabled');
+
+ if (options.minDate && options.minDate.isAfter(startYear, 'y')) {
+ yearsViewHeader.eq(0).addClass('disabled');
+ }
+
+ yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year());
+
+ if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) {
+ yearsViewHeader.eq(2).addClass('disabled');
+ }
+
+ while (!startYear.isAfter(endYear, 'y')) {
+ html += '<span data-action="selectYear" class="year' + (startYear.isSame(date, 'y') && !unset ? ' active' : '') + (!isValid(startYear, 'y') ? ' disabled' : '') + '">' + startYear.year() + '</span>';
+ startYear.add(1, 'y');
+ }
+
+ yearsView.find('td').html(html);
+ },
+
+ updateDecades = function () {
+ var decadesView = widget.find('.datepicker-decades'),
+ decadesViewHeader = decadesView.find('th'),
+ startDecade = moment({y: viewDate.year() - (viewDate.year() % 100) - 1}),
+ endDecade = startDecade.clone().add(100, 'y'),
+ startedAt = startDecade.clone(),
+ html = '';
+
+ decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury);
+ decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury);
+
+ decadesView.find('.disabled').removeClass('disabled');
+
+ if (startDecade.isSame(moment({y: 1900})) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) {
+ decadesViewHeader.eq(0).addClass('disabled');
+ }
+
+ decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year());
+
+ if (startDecade.isSame(moment({y: 2000})) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) {
+ decadesViewHeader.eq(2).addClass('disabled');
+ }
+
+ while (!startDecade.isAfter(endDecade, 'y')) {
+ html += '<span data-action="selectDecade" class="decade' + (startDecade.isSame(date, 'y') ? ' active' : '') +
+ (!isValid(startDecade, 'y') ? ' disabled' : '') + '" data-selection="' + (startDecade.year() + 6) + '">' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + '</span>';
+ startDecade.add(12, 'y');
+ }
+ html += '<span></span><span></span><span></span>'; //push the dangling block over, at least this way it's even
+
+ decadesView.find('td').html(html);
+ decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year()));
+ },
+
+ fillDate = function () {
+ var daysView = widget.find('.datepicker-days'),
+ daysViewHeader = daysView.find('th'),
+ currentDate,
+ html = [],
+ row,
+ clsName,
+ i;
+
+ if (!hasDate()) {
+ return;
+ }
+
+ daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth);
+ daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
+ daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth);
+
+ daysView.find('.disabled').removeClass('disabled');
+ daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat));
+
+ if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) {
+ daysViewHeader.eq(0).addClass('disabled');
+ }
+ if (!isValid(viewDate.clone().add(1, 'M'), 'M')) {
+ daysViewHeader.eq(2).addClass('disabled');
+ }
+
+ currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d');
+
+ for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks)
+ if (currentDate.weekday() === 0) {
+ row = $('<tr>');
+ if (options.calendarWeeks) {
+ row.append('<td class="cw">' + currentDate.week() + '</td>');
+ }
+ html.push(row);
+ }
+ clsName = '';
+ if (currentDate.isBefore(viewDate, 'M')) {
+ clsName += ' old';
+ }
+ if (currentDate.isAfter(viewDate, 'M')) {
+ clsName += ' new';
+ }
+ if (currentDate.isSame(date, 'd') && !unset) {
+ clsName += ' active';
+ }
+ if (!isValid(currentDate, 'd')) {
+ clsName += ' disabled';
+ }
+ if (currentDate.isSame(getMoment(), 'd')) {
+ clsName += ' today';
+ }
+ if (currentDate.day() === 0 || currentDate.day() === 6) {
+ clsName += ' weekend';
+ }
+ row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="day' + clsName + '">' + currentDate.date() + '</td>');
+ currentDate.add(1, 'd');
+ }
+
+ daysView.find('tbody').empty().append(html);
+
+ updateMonths();
+
+ updateYears();
+
+ updateDecades();
+ },
+
+ fillHours = function () {
+ var table = widget.find('.timepicker-hours table'),
+ currentHour = viewDate.clone().startOf('d'),
+ html = [],
+ row = $('<tr>');
+
+ if (viewDate.hour() > 11 && !use24Hours) {
+ currentHour.hour(12);
+ }
+ while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) {
+ if (currentHour.hour() % 4 === 0) {
+ row = $('<tr>');
+ html.push(row);
+ }
+ row.append('<td data-action="selectHour" class="hour' + (!isValid(currentHour, 'h') ? ' disabled' : '') + '">' + currentHour.format(use24Hours ? 'HH' : 'hh') + '</td>');
+ currentHour.add(1, 'h');
+ }
+ table.empty().append(html);
+ },
+
+ fillMinutes = function () {
+ var table = widget.find('.timepicker-minutes table'),
+ currentMinute = viewDate.clone().startOf('h'),
+ html = [],
+ row = $('<tr>'),
+ step = options.stepping === 1 ? 5 : options.stepping;
+
+ while (viewDate.isSame(currentMinute, 'h')) {
+ if (currentMinute.minute() % (step * 4) === 0) {
+ row = $('<tr>');
+ html.push(row);
+ }
+ row.append('<td data-action="selectMinute" class="minute' + (!isValid(currentMinute, 'm') ? ' disabled' : '') + '">' + currentMinute.format('mm') + '</td>');
+ currentMinute.add(step, 'm');
+ }
+ table.empty().append(html);
+ },
+
+ fillSeconds = function () {
+ var table = widget.find('.timepicker-seconds table'),
+ currentSecond = viewDate.clone().startOf('m'),
+ html = [],
+ row = $('<tr>');
+
+ while (viewDate.isSame(currentSecond, 'm')) {
+ if (currentSecond.second() % 20 === 0) {
+ row = $('<tr>');
+ html.push(row);
+ }
+ row.append('<td data-action="selectSecond" class="second' + (!isValid(currentSecond, 's') ? ' disabled' : '') + '">' + currentSecond.format('ss') + '</td>');
+ currentSecond.add(5, 's');
+ }
+
+ table.empty().append(html);
+ },
+
+ fillTime = function () {
+ var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]');
+
+ if (!use24Hours) {
+ toggle = widget.find('.timepicker [data-action=togglePeriod]');
+ newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h');
+
+ toggle.text(date.format('A'));
+
+ if (isValid(newDate, 'h')) {
+ toggle.removeClass('disabled');
+ } else {
+ toggle.addClass('disabled');
+ }
+ }
+ timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh'));
+ timeComponents.filter('[data-time-component=minutes]').text(date.format('mm'));
+ timeComponents.filter('[data-time-component=seconds]').text(date.format('ss'));
+
+ fillHours();
+ fillMinutes();
+ fillSeconds();
+ },
+
+ update = function () {
+ if (!widget) {
+ return;
+ }
+ fillDate();
+ fillTime();
+ },
+
+ setValue = function (targetMoment) {
+ var oldDate = unset ? null : date;
+
+ // case of calling setValue(null or false)
+ if (!targetMoment) {
+ unset = true;
+ input.val('');
+ element.data('date', '');
+ notifyEvent({
+ type: 'dp.change',
+ date: false,
+ oldDate: oldDate
+ });
+ update();
+ return;
+ }
+
+ targetMoment = targetMoment.clone().locale(options.locale);
+
+ if (options.stepping !== 1) {
+ targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping) % 60).seconds(0);
+ }
+
+ if (isValid(targetMoment)) {
+ date = targetMoment;
+ viewDate = date.clone();
+ input.val(date.format(actualFormat));
+ element.data('date', date.format(actualFormat));
+ unset = false;
+ update();
+ notifyEvent({
+ type: 'dp.change',
+ date: date.clone(),
+ oldDate: oldDate
+ });
+ } else {
+ if (!options.keepInvalid) {
+ input.val(unset ? '' : date.format(actualFormat));
+ }
+ notifyEvent({
+ type: 'dp.error',
+ date: targetMoment
+ });
+ }
+ },
+
+ hide = function () {
+ ///<summary>Hides the widget. Possibly will emit dp.hide</summary>
+ var transitioning = false;
+ if (!widget) {
+ return picker;
+ }
+ // Ignore event if in the middle of a picker transition
+ widget.find('.collapse').each(function () {
+ var collapseData = $(this).data('collapse');
+ if (collapseData && collapseData.transitioning) {
+ transitioning = true;
+ return false;
+ }
+ return true;
+ });
+ if (transitioning) {
+ return picker;
+ }
+ if (component && component.hasClass('btn')) {
+ component.toggleClass('active');
+ }
+ widget.hide();
+
+ $(window).off('resize', place);
+ widget.off('click', '[data-action]');
+ widget.off('mousedown', false);
+
+ widget.remove();
+ widget = false;
+
+ notifyEvent({
+ type: 'dp.hide',
+ date: date.clone()
+ });
+
+ input.blur();
+
+ return picker;
+ },
+
+ clear = function () {
+ setValue(null);
+ },
+
+ /********************************************************************************
+ *
+ * Widget UI interaction functions
+ *
+ ********************************************************************************/
+ actions = {
+ next: function () {
+ var navFnc = datePickerModes[currentViewMode].navFnc;
+ viewDate.add(datePickerModes[currentViewMode].navStep, navFnc);
+ fillDate();
+ viewUpdate(navFnc);
+ },
+
+ previous: function () {
+ var navFnc = datePickerModes[currentViewMode].navFnc;
+ viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc);
+ fillDate();
+ viewUpdate(navFnc);
+ },
+
+ pickerSwitch: function () {
+ showMode(1);
+ },
+
+ selectMonth: function (e) {
+ var month = $(e.target).closest('tbody').find('span').index($(e.target));
+ viewDate.month(month);
+ if (currentViewMode === minViewModeNumber) {
+ setValue(date.clone().year(viewDate.year()).month(viewDate.month()));
+ if (!options.inline) {
+ hide();
+ }
+ } else {
+ showMode(-1);
+ fillDate();
+ }
+ viewUpdate('M');
+ },
+
+ selectYear: function (e) {
+ var year = parseInt($(e.target).text(), 10) || 0;
+ viewDate.year(year);
+ if (currentViewMode === minViewModeNumber) {
+ setValue(date.clone().year(viewDate.year()));
+ if (!options.inline) {
+ hide();
+ }
+ } else {
+ showMode(-1);
+ fillDate();
+ }
+ viewUpdate('YYYY');
+ },
+
+ selectDecade: function (e) {
+ var year = parseInt($(e.target).data('selection'), 10) || 0;
+ viewDate.year(year);
+ if (currentViewMode === minViewModeNumber) {
+ setValue(date.clone().year(viewDate.year()));
+ if (!options.inline) {
+ hide();
+ }
+ } else {
+ showMode(-1);
+ fillDate();
+ }
+ viewUpdate('YYYY');
+ },
+
+ selectDay: function (e) {
+ var day = viewDate.clone();
+ if ($(e.target).is('.old')) {
+ day.subtract(1, 'M');
+ }
+ if ($(e.target).is('.new')) {
+ day.add(1, 'M');
+ }
+ setValue(day.date(parseInt($(e.target).text(), 10)));
+ if (!hasTime() && !options.keepOpen && !options.inline) {
+ hide();
+ }
+ },
+
+ incrementHours: function () {
+ var newDate = date.clone().add(1, 'h');
+ if (isValid(newDate, 'h')) {
+ setValue(newDate);
+ }
+ },
+
+ incrementMinutes: function () {
+ var newDate = date.clone().add(options.stepping, 'm');
+ if (isValid(newDate, 'm')) {
+ setValue(newDate);
+ }
+ },
+
+ incrementSeconds: function () {
+ var newDate = date.clone().add(1, 's');
+ if (isValid(newDate, 's')) {
+ setValue(newDate);
+ }
+ },
+
+ decrementHours: function () {
+ var newDate = date.clone().subtract(1, 'h');
+ if (isValid(newDate, 'h')) {
+ setValue(newDate);
+ }
+ },
+
+ decrementMinutes: function () {
+ var newDate = date.clone().subtract(options.stepping, 'm');
+ if (isValid(newDate, 'm')) {
+ setValue(newDate);
+ }
+ },
+
+ decrementSeconds: function () {
+ var newDate = date.clone().subtract(1, 's');
+ if (isValid(newDate, 's')) {
+ setValue(newDate);
+ }
+ },
+
+ togglePeriod: function () {
+ setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'));
+ },
+
+ togglePicker: function (e) {
+ var $this = $(e.target),
+ $parent = $this.closest('ul'),
+ expanded = $parent.find('.in'),
+ closed = $parent.find('.collapse:not(.in)'),
+ collapseData;
+
+ if (expanded && expanded.length) {
+ collapseData = expanded.data('collapse');
+ if (collapseData && collapseData.transitioning) {
+ return;
+ }
+ if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it
+ expanded.collapse('hide');
+ closed.collapse('show');
+ } else { // otherwise just toggle in class on the two views
+ expanded.removeClass('in');
+ closed.addClass('in');
+ }
+ if ($this.is('span')) {
+ $this.toggleClass(options.icons.time + ' ' + options.icons.date);
+ } else {
+ $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
+ }
+
+ // NOTE: uncomment if toggled state will be restored in show()
+ //if (component) {
+ // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
+ //}
+ }
+ },
+
+ showPicker: function () {
+ widget.find('.timepicker > div:not(.timepicker-picker)').hide();
+ widget.find('.timepicker .timepicker-picker').show();
+ },
+
+ showHours: function () {
+ widget.find('.timepicker .timepicker-picker').hide();
+ widget.find('.timepicker .timepicker-hours').show();
+ },
+
+ showMinutes: function () {
+ widget.find('.timepicker .timepicker-picker').hide();
+ widget.find('.timepicker .timepicker-minutes').show();
+ },
+
+ showSeconds: function () {
+ widget.find('.timepicker .timepicker-picker').hide();
+ widget.find('.timepicker .timepicker-seconds').show();
+ },
+
+ selectHour: function (e) {
+ var hour = parseInt($(e.target).text(), 10);
+
+ if (!use24Hours) {
+ if (date.hours() >= 12) {
+ if (hour !== 12) {
+ hour += 12;
+ }
+ } else {
+ if (hour === 12) {
+ hour = 0;
+ }
+ }
+ }
+ setValue(date.clone().hours(hour));
+ actions.showPicker.call(picker);
+ },
+
+ selectMinute: function (e) {
+ setValue(date.clone().minutes(parseInt($(e.target).text(), 10)));
+ actions.showPicker.call(picker);
+ },
+
+ selectSecond: function (e) {
+ setValue(date.clone().seconds(parseInt($(e.target).text(), 10)));
+ actions.showPicker.call(picker);
+ },
+
+ clear: clear,
+
+ today: function () {
+ var todaysDate = getMoment();
+ if (isValid(todaysDate, 'd')) {
+ setValue(todaysDate);
+ }
+ },
+
+ close: hide
+ },
+
+ doAction = function (e) {
+ if ($(e.currentTarget).is('.disabled')) {
+ return false;
+ }
+ actions[$(e.currentTarget).data('action')].apply(picker, arguments);
+ return false;
+ },
+
+ show = function () {
+ ///<summary>Shows the widget. Possibly will emit dp.show and dp.change</summary>
+ var currentMoment,
+ useCurrentGranularity = {
+ 'year': function (m) {
+ return m.month(0).date(1).hours(0).seconds(0).minutes(0);
+ },
+ 'month': function (m) {
+ return m.date(1).hours(0).seconds(0).minutes(0);
+ },
+ 'day': function (m) {
+ return m.hours(0).seconds(0).minutes(0);
+ },
+ 'hour': function (m) {
+ return m.seconds(0).minutes(0);
+ },
+ 'minute': function (m) {
+ return m.seconds(0);
+ }
+ };
+
+ if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) {
+ return picker;
+ }
+ if (input.val() !== undefined && input.val().trim().length !== 0) {
+ setValue(parseInputDate(input.val().trim()));
+ } else if (options.useCurrent && unset && ((input.is('input') && input.val().trim().length === 0) || options.inline)) {
+ currentMoment = getMoment();
+ if (typeof options.useCurrent === 'string') {
+ currentMoment = useCurrentGranularity[options.useCurrent](currentMoment);
+ }
+ setValue(currentMoment);
+ }
+
+ widget = getTemplate();
+
+ fillDow();
+ fillMonths();
+
+ widget.find('.timepicker-hours').hide();
+ widget.find('.timepicker-minutes').hide();
+ widget.find('.timepicker-seconds').hide();
+
+ update();
+ showMode();
+
+ $(window).on('resize', place);
+ widget.on('click', '[data-action]', doAction); // this handles clicks on the widget
+ widget.on('mousedown', false);
+
+ if (component && component.hasClass('btn')) {
+ component.toggleClass('active');
+ }
+ widget.show();
+ place();
+
+ if (options.focusOnShow && !input.is(':focus')) {
+ input.focus();
+ }
+
+ notifyEvent({
+ type: 'dp.show'
+ });
+ return picker;
+ },
+
+ toggle = function () {
+ /// <summary>Shows or hides the widget</summary>
+ return (widget ? hide() : show());
+ },
+
+ parseInputDate = function (inputDate) {
+ if (options.parseInputDate === undefined) {
+ if (moment.isMoment(inputDate) || inputDate instanceof Date) {
+ inputDate = moment(inputDate);
+ } else {
+ inputDate = getMoment(inputDate);
+ }
+ } else {
+ inputDate = options.parseInputDate(inputDate);
+ }
+ inputDate.locale(options.locale);
+ return inputDate;
+ },
+
+ keydown = function (e) {
+ var handler = null,
+ index,
+ index2,
+ pressedKeys = [],
+ pressedModifiers = {},
+ currentKey = e.which,
+ keyBindKeys,
+ allModifiersPressed,
+ pressed = 'p';
+
+ keyState[currentKey] = pressed;
+
+ for (index in keyState) {
+ if (keyState.hasOwnProperty(index) && keyState[index] === pressed) {
+ pressedKeys.push(index);
+ if (parseInt(index, 10) !== currentKey) {
+ pressedModifiers[index] = true;
+ }
+ }
+ }
+
+ for (index in options.keyBinds) {
+ if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') {
+ keyBindKeys = index.split(' ');
+ if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) {
+ allModifiersPressed = true;
+ for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) {
+ if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) {
+ allModifiersPressed = false;
+ break;
+ }
+ }
+ if (allModifiersPressed) {
+ handler = options.keyBinds[index];
+ break;
+ }
+ }
+ }
+ }
+
+ if (handler) {
+ handler.call(picker, widget);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ },
+
+ keyup = function (e) {
+ keyState[e.which] = 'r';
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ change = function (e) {
+ var val = $(e.target).val().trim(),
+ parsedDate = val ? parseInputDate(val) : null;
+ setValue(parsedDate);
+ e.stopImmediatePropagation();
+ return false;
+ },
+
+ attachDatePickerElementEvents = function () {
+ input.on({
+ 'change': change,
+ 'blur': options.debug ? '' : hide,
+ 'keydown': keydown,
+ 'keyup': keyup,
+ 'focus': options.allowInputToggle ? show : ''
+ });
+
+ if (element.is('input')) {
+ input.on({
+ 'focus': show
+ });
+ } else if (component) {
+ component.on('click', toggle);
+ component.on('mousedown', false);
+ }
+ },
+
+ detachDatePickerElementEvents = function () {
+ input.off({
+ 'change': change,
+ 'blur': blur,
+ 'keydown': keydown,
+ 'keyup': keyup,
+ 'focus': options.allowInputToggle ? hide : ''
+ });
+
+ if (element.is('input')) {
+ input.off({
+ 'focus': show
+ });
+ } else if (component) {
+ component.off('click', toggle);
+ component.off('mousedown', false);
+ }
+ },
+
+ indexGivenDates = function (givenDatesArray) {
+ // Store given enabledDates and disabledDates as keys.
+ // This way we can check their existence in O(1) time instead of looping through whole array.
+ // (for example: options.enabledDates['2014-02-27'] === true)
+ var givenDatesIndexed = {};
+ $.each(givenDatesArray, function () {
+ var dDate = parseInputDate(this);
+ if (dDate.isValid()) {
+ givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true;
+ }
+ });
+ return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false;
+ },
+
+ indexGivenHours = function (givenHoursArray) {
+ // Store given enabledHours and disabledHours as keys.
+ // This way we can check their existence in O(1) time instead of looping through whole array.
+ // (for example: options.enabledHours['2014-02-27'] === true)
+ var givenHoursIndexed = {};
+ $.each(givenHoursArray, function () {
+ givenHoursIndexed[this] = true;
+ });
+ return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false;
+ },
+
+ initFormatting = function () {
+ var format = options.format || 'L LT';
+
+ actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) {
+ var newinput = date.localeData().longDateFormat(formatInput) || formatInput;
+ return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740
+ return date.localeData().longDateFormat(formatInput2) || formatInput2;
+ });
+ });
+
+
+ parseFormats = options.extraFormats ? options.extraFormats.slice() : [];
+ if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) {
+ parseFormats.push(actualFormat);
+ }
+
+ use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1);
+
+ if (isEnabled('y')) {
+ minViewModeNumber = 2;
+ }
+ if (isEnabled('M')) {
+ minViewModeNumber = 1;
+ }
+ if (isEnabled('d')) {
+ minViewModeNumber = 0;
+ }
+
+ currentViewMode = Math.max(minViewModeNumber, currentViewMode);
+
+ if (!unset) {
+ setValue(date);
+ }
+ };
+
+ /********************************************************************************
+ *
+ * Public API functions
+ * =====================
+ *
+ * Important: Do not expose direct references to private objects or the options
+ * object to the outer world. Always return a clone when returning values or make
+ * a clone when setting a private variable.
+ *
+ ********************************************************************************/
+ picker.destroy = function () {
+ ///<summary>Destroys the widget and removes all attached event listeners</summary>
+ hide();
+ detachDatePickerElementEvents();
+ element.removeData('DateTimePicker');
+ element.removeData('date');
+ };
+
+ picker.toggle = toggle;
+
+ picker.show = show;
+
+ picker.hide = hide;
+
+ picker.disable = function () {
+ ///<summary>Disables the input element, the component is attached to, by adding a disabled="true" attribute to it.
+ ///If the widget was visible before that call it is hidden. Possibly emits dp.hide</summary>
+ hide();
+ if (component && component.hasClass('btn')) {
+ component.addClass('disabled');
+ }
+ input.prop('disabled', true);
+ return picker;
+ };
+
+ picker.enable = function () {
+ ///<summary>Enables the input element, the component is attached to, by removing disabled attribute from it.</summary>
+ if (component && component.hasClass('btn')) {
+ component.removeClass('disabled');
+ }
+ input.prop('disabled', false);
+ return picker;
+ };
+
+ picker.ignoreReadonly = function (ignoreReadonly) {
+ if (arguments.length === 0) {
+ return options.ignoreReadonly;
+ }
+ if (typeof ignoreReadonly !== 'boolean') {
+ throw new TypeError('ignoreReadonly () expects a boolean parameter');
+ }
+ options.ignoreReadonly = ignoreReadonly;
+ return picker;
+ };
+
+ picker.options = function (newOptions) {
+ if (arguments.length === 0) {
+ return $.extend(true, {}, options);
+ }
+
+ if (!(newOptions instanceof Object)) {
+ throw new TypeError('options() options parameter should be an object');
+ }
+ $.extend(true, options, newOptions);
+ $.each(options, function (key, value) {
+ if (picker[key] !== undefined) {
+ picker[key](value);
+ } else {
+ throw new TypeError('option ' + key + ' is not recognized!');
+ }
+ });
+ return picker;
+ };
+
+ picker.date = function (newDate) {
+ ///<signature helpKeyword="$.fn.datetimepicker.date">
+ ///<summary>Returns the component's model current date, a moment object or null if not set.</summary>
+ ///<returns type="Moment">date.clone()</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.</summary>
+ ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, Date, moment, null parameter.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ if (unset) {
+ return null;
+ }
+ return date.clone();
+ }
+
+ if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
+ throw new TypeError('date() parameter must be one of [null, string, moment or Date]');
+ }
+
+ setValue(newDate === null ? null : parseInputDate(newDate));
+ return picker;
+ };
+
+ picker.format = function (newFormat) {
+ ///<summary>test su</summary>
+ ///<param name="newFormat">info about para</param>
+ ///<returns type="string|boolean">returns foo</returns>
+ if (arguments.length === 0) {
+ return options.format;
+ }
+
+ if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) {
+ throw new TypeError('format() expects a sting or boolean:false parameter ' + newFormat);
+ }
+
+ options.format = newFormat;
+ if (actualFormat) {
+ initFormatting(); // reinit formatting
+ }
+ return picker;
+ };
+
+ picker.timeZone = function (newZone) {
+ if (arguments.length === 0) {
+ return options.timeZone;
+ }
+
+ options.timeZone = newZone;
+
+ return picker;
+ };
+
+ picker.dayViewHeaderFormat = function (newFormat) {
+ if (arguments.length === 0) {
+ return options.dayViewHeaderFormat;
+ }
+
+ if (typeof newFormat !== 'string') {
+ throw new TypeError('dayViewHeaderFormat() expects a string parameter');
+ }
+
+ options.dayViewHeaderFormat = newFormat;
+ return picker;
+ };
+
+ picker.extraFormats = function (formats) {
+ if (arguments.length === 0) {
+ return options.extraFormats;
+ }
+
+ if (formats !== false && !(formats instanceof Array)) {
+ throw new TypeError('extraFormats() expects an array or false parameter');
+ }
+
+ options.extraFormats = formats;
+ if (parseFormats) {
+ initFormatting(); // reinit formatting
+ }
+ return picker;
+ };
+
+ picker.disabledDates = function (dates) {
+ ///<signature helpKeyword="$.fn.datetimepicker.disabledDates">
+ ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
+ ///<returns type="array">options.disabledDates</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
+ ///options.enabledDates if such exist.</summary>
+ ///<param name="dates" locid="$.fn.datetimepicker.disabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates);
+ }
+
+ if (!dates) {
+ options.disabledDates = false;
+ update();
+ return picker;
+ }
+ if (!(dates instanceof Array)) {
+ throw new TypeError('disabledDates() expects an array parameter');
+ }
+ options.disabledDates = indexGivenDates(dates);
+ options.enabledDates = false;
+ update();
+ return picker;
+ };
+
+ picker.enabledDates = function (dates) {
+ ///<signature helpKeyword="$.fn.datetimepicker.enabledDates">
+ ///<summary>Returns an array with the currently set enabled dates on the component.</summary>
+ ///<returns type="array">options.enabledDates</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist.</summary>
+ ///<param name="dates" locid="$.fn.datetimepicker.enabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates);
+ }
+
+ if (!dates) {
+ options.enabledDates = false;
+ update();
+ return picker;
+ }
+ if (!(dates instanceof Array)) {
+ throw new TypeError('enabledDates() expects an array parameter');
+ }
+ options.enabledDates = indexGivenDates(dates);
+ options.disabledDates = false;
+ update();
+ return picker;
+ };
+
+ picker.daysOfWeekDisabled = function (daysOfWeekDisabled) {
+ if (arguments.length === 0) {
+ return options.daysOfWeekDisabled.splice(0);
+ }
+
+ if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) {
+ options.daysOfWeekDisabled = false;
+ update();
+ return picker;
+ }
+
+ if (!(daysOfWeekDisabled instanceof Array)) {
+ throw new TypeError('daysOfWeekDisabled() expects an array parameter');
+ }
+ options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) {
+ currentValue = parseInt(currentValue, 10);
+ if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) {
+ return previousValue;
+ }
+ if (previousValue.indexOf(currentValue) === -1) {
+ previousValue.push(currentValue);
+ }
+ return previousValue;
+ }, []).sort();
+ if (options.useCurrent && !options.keepInvalid) {
+ var tries = 0;
+ while (!isValid(date, 'd')) {
+ date.add(1, 'd');
+ if (tries === 7) {
+ throw 'Tried 7 times to find a valid date';
+ }
+ tries++;
+ }
+ setValue(date);
+ }
+ update();
+ return picker;
+ };
+
+ picker.maxDate = function (maxDate) {
+ if (arguments.length === 0) {
+ return options.maxDate ? options.maxDate.clone() : options.maxDate;
+ }
+
+ if ((typeof maxDate === 'boolean') && maxDate === false) {
+ options.maxDate = false;
+ update();
+ return picker;
+ }
+
+ if (typeof maxDate === 'string') {
+ if (maxDate === 'now' || maxDate === 'moment') {
+ maxDate = getMoment();
+ }
+ }
+
+ var parsedDate = parseInputDate(maxDate);
+
+ if (!parsedDate.isValid()) {
+ throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate);
+ }
+ if (options.minDate && parsedDate.isBefore(options.minDate)) {
+ throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat));
+ }
+ options.maxDate = parsedDate;
+ if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) {
+ setValue(options.maxDate);
+ }
+ if (viewDate.isAfter(parsedDate)) {
+ viewDate = parsedDate.clone().subtract(options.stepping, 'm');
+ }
+ update();
+ return picker;
+ };
+
+ picker.minDate = function (minDate) {
+ if (arguments.length === 0) {
+ return options.minDate ? options.minDate.clone() : options.minDate;
+ }
+
+ if ((typeof minDate === 'boolean') && minDate === false) {
+ options.minDate = false;
+ update();
+ return picker;
+ }
+
+ if (typeof minDate === 'string') {
+ if (minDate === 'now' || minDate === 'moment') {
+ minDate = getMoment();
+ }
+ }
+
+ var parsedDate = parseInputDate(minDate);
+
+ if (!parsedDate.isValid()) {
+ throw new TypeError('minDate() Could not parse date parameter: ' + minDate);
+ }
+ if (options.maxDate && parsedDate.isAfter(options.maxDate)) {
+ throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat));
+ }
+ options.minDate = parsedDate;
+ if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) {
+ setValue(options.minDate);
+ }
+ if (viewDate.isBefore(parsedDate)) {
+ viewDate = parsedDate.clone().add(options.stepping, 'm');
+ }
+ update();
+ return picker;
+ };
+
+ picker.defaultDate = function (defaultDate) {
+ ///<signature helpKeyword="$.fn.datetimepicker.defaultDate">
+ ///<summary>Returns a moment with the options.defaultDate option configuration or false if not set</summary>
+ ///<returns type="Moment">date.clone()</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared.</summary>
+ ///<param name="defaultDate" locid="$.fn.datetimepicker.defaultDate_p:defaultDate">Takes a string, Date, moment, boolean:false</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return options.defaultDate ? options.defaultDate.clone() : options.defaultDate;
+ }
+ if (!defaultDate) {
+ options.defaultDate = false;
+ return picker;
+ }
+
+ if (typeof defaultDate === 'string') {
+ if (defaultDate === 'now' || defaultDate === 'moment') {
+ defaultDate = getMoment();
+ }
+ }
+
+ var parsedDate = parseInputDate(defaultDate);
+ if (!parsedDate.isValid()) {
+ throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate);
+ }
+ if (!isValid(parsedDate)) {
+ throw new TypeError('defaultDate() date passed is invalid according to component setup validations');
+ }
+
+ options.defaultDate = parsedDate;
+
+ if ((options.defaultDate && options.inline) || input.val().trim() === '') {
+ setValue(options.defaultDate);
+ }
+ return picker;
+ };
+
+ picker.locale = function (locale) {
+ if (arguments.length === 0) {
+ return options.locale;
+ }
+
+ if (!moment.localeData(locale)) {
+ throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!');
+ }
+
+ options.locale = locale;
+ date.locale(options.locale);
+ viewDate.locale(options.locale);
+
+ if (actualFormat) {
+ initFormatting(); // reinit formatting
+ }
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.stepping = function (stepping) {
+ if (arguments.length === 0) {
+ return options.stepping;
+ }
+
+ stepping = parseInt(stepping, 10);
+ if (isNaN(stepping) || stepping < 1) {
+ stepping = 1;
+ }
+ options.stepping = stepping;
+ return picker;
+ };
+
+ picker.useCurrent = function (useCurrent) {
+ var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
+ if (arguments.length === 0) {
+ return options.useCurrent;
+ }
+
+ if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) {
+ throw new TypeError('useCurrent() expects a boolean or string parameter');
+ }
+ if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) {
+ throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', '));
+ }
+ options.useCurrent = useCurrent;
+ return picker;
+ };
+
+ picker.collapse = function (collapse) {
+ if (arguments.length === 0) {
+ return options.collapse;
+ }
+
+ if (typeof collapse !== 'boolean') {
+ throw new TypeError('collapse() expects a boolean parameter');
+ }
+ if (options.collapse === collapse) {
+ return picker;
+ }
+ options.collapse = collapse;
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.icons = function (icons) {
+ if (arguments.length === 0) {
+ return $.extend({}, options.icons);
+ }
+
+ if (!(icons instanceof Object)) {
+ throw new TypeError('icons() expects parameter to be an Object');
+ }
+ $.extend(options.icons, icons);
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.tooltips = function (tooltips) {
+ if (arguments.length === 0) {
+ return $.extend({}, options.tooltips);
+ }
+
+ if (!(tooltips instanceof Object)) {
+ throw new TypeError('tooltips() expects parameter to be an Object');
+ }
+ $.extend(options.tooltips, tooltips);
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.useStrict = function (useStrict) {
+ if (arguments.length === 0) {
+ return options.useStrict;
+ }
+
+ if (typeof useStrict !== 'boolean') {
+ throw new TypeError('useStrict() expects a boolean parameter');
+ }
+ options.useStrict = useStrict;
+ return picker;
+ };
+
+ picker.sideBySide = function (sideBySide) {
+ if (arguments.length === 0) {
+ return options.sideBySide;
+ }
+
+ if (typeof sideBySide !== 'boolean') {
+ throw new TypeError('sideBySide() expects a boolean parameter');
+ }
+ options.sideBySide = sideBySide;
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.viewMode = function (viewMode) {
+ if (arguments.length === 0) {
+ return options.viewMode;
+ }
+
+ if (typeof viewMode !== 'string') {
+ throw new TypeError('viewMode() expects a string parameter');
+ }
+
+ if (viewModes.indexOf(viewMode) === -1) {
+ throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value');
+ }
+
+ options.viewMode = viewMode;
+ currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber);
+
+ showMode();
+ return picker;
+ };
+
+ picker.toolbarPlacement = function (toolbarPlacement) {
+ if (arguments.length === 0) {
+ return options.toolbarPlacement;
+ }
+
+ if (typeof toolbarPlacement !== 'string') {
+ throw new TypeError('toolbarPlacement() expects a string parameter');
+ }
+ if (toolbarPlacements.indexOf(toolbarPlacement) === -1) {
+ throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value');
+ }
+ options.toolbarPlacement = toolbarPlacement;
+
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.widgetPositioning = function (widgetPositioning) {
+ if (arguments.length === 0) {
+ return $.extend({}, options.widgetPositioning);
+ }
+
+ if (({}).toString.call(widgetPositioning) !== '[object Object]') {
+ throw new TypeError('widgetPositioning() expects an object variable');
+ }
+ if (widgetPositioning.horizontal) {
+ if (typeof widgetPositioning.horizontal !== 'string') {
+ throw new TypeError('widgetPositioning() horizontal variable must be a string');
+ }
+ widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase();
+ if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) {
+ throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')');
+ }
+ options.widgetPositioning.horizontal = widgetPositioning.horizontal;
+ }
+ if (widgetPositioning.vertical) {
+ if (typeof widgetPositioning.vertical !== 'string') {
+ throw new TypeError('widgetPositioning() vertical variable must be a string');
+ }
+ widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase();
+ if (verticalModes.indexOf(widgetPositioning.vertical) === -1) {
+ throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')');
+ }
+ options.widgetPositioning.vertical = widgetPositioning.vertical;
+ }
+ update();
+ return picker;
+ };
+
+ picker.calendarWeeks = function (calendarWeeks) {
+ if (arguments.length === 0) {
+ return options.calendarWeeks;
+ }
+
+ if (typeof calendarWeeks !== 'boolean') {
+ throw new TypeError('calendarWeeks() expects parameter to be a boolean value');
+ }
+
+ options.calendarWeeks = calendarWeeks;
+ update();
+ return picker;
+ };
+
+ picker.showTodayButton = function (showTodayButton) {
+ if (arguments.length === 0) {
+ return options.showTodayButton;
+ }
+
+ if (typeof showTodayButton !== 'boolean') {
+ throw new TypeError('showTodayButton() expects a boolean parameter');
+ }
+
+ options.showTodayButton = showTodayButton;
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.showClear = function (showClear) {
+ if (arguments.length === 0) {
+ return options.showClear;
+ }
+
+ if (typeof showClear !== 'boolean') {
+ throw new TypeError('showClear() expects a boolean parameter');
+ }
+
+ options.showClear = showClear;
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.widgetParent = function (widgetParent) {
+ if (arguments.length === 0) {
+ return options.widgetParent;
+ }
+
+ if (typeof widgetParent === 'string') {
+ widgetParent = $(widgetParent);
+ }
+
+ if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) {
+ throw new TypeError('widgetParent() expects a string or a jQuery object parameter');
+ }
+
+ options.widgetParent = widgetParent;
+ if (widget) {
+ hide();
+ show();
+ }
+ return picker;
+ };
+
+ picker.keepOpen = function (keepOpen) {
+ if (arguments.length === 0) {
+ return options.keepOpen;
+ }
+
+ if (typeof keepOpen !== 'boolean') {
+ throw new TypeError('keepOpen() expects a boolean parameter');
+ }
+
+ options.keepOpen = keepOpen;
+ return picker;
+ };
+
+ picker.focusOnShow = function (focusOnShow) {
+ if (arguments.length === 0) {
+ return options.focusOnShow;
+ }
+
+ if (typeof focusOnShow !== 'boolean') {
+ throw new TypeError('focusOnShow() expects a boolean parameter');
+ }
+
+ options.focusOnShow = focusOnShow;
+ return picker;
+ };
+
+ picker.inline = function (inline) {
+ if (arguments.length === 0) {
+ return options.inline;
+ }
+
+ if (typeof inline !== 'boolean') {
+ throw new TypeError('inline() expects a boolean parameter');
+ }
+
+ options.inline = inline;
+ return picker;
+ };
+
+ picker.clear = function () {
+ clear();
+ return picker;
+ };
+
+ picker.keyBinds = function (keyBinds) {
+ options.keyBinds = keyBinds;
+ return picker;
+ };
+
+ picker.getMoment = function (d) {
+ return getMoment(d);
+ };
+
+ picker.debug = function (debug) {
+ if (typeof debug !== 'boolean') {
+ throw new TypeError('debug() expects a boolean parameter');
+ }
+
+ options.debug = debug;
+ return picker;
+ };
+
+ picker.allowInputToggle = function (allowInputToggle) {
+ if (arguments.length === 0) {
+ return options.allowInputToggle;
+ }
+
+ if (typeof allowInputToggle !== 'boolean') {
+ throw new TypeError('allowInputToggle() expects a boolean parameter');
+ }
+
+ options.allowInputToggle = allowInputToggle;
+ return picker;
+ };
+
+ picker.showClose = function (showClose) {
+ if (arguments.length === 0) {
+ return options.showClose;
+ }
+
+ if (typeof showClose !== 'boolean') {
+ throw new TypeError('showClose() expects a boolean parameter');
+ }
+
+ options.showClose = showClose;
+ return picker;
+ };
+
+ picker.keepInvalid = function (keepInvalid) {
+ if (arguments.length === 0) {
+ return options.keepInvalid;
+ }
+
+ if (typeof keepInvalid !== 'boolean') {
+ throw new TypeError('keepInvalid() expects a boolean parameter');
+ }
+ options.keepInvalid = keepInvalid;
+ return picker;
+ };
+
+ picker.datepickerInput = function (datepickerInput) {
+ if (arguments.length === 0) {
+ return options.datepickerInput;
+ }
+
+ if (typeof datepickerInput !== 'string') {
+ throw new TypeError('datepickerInput() expects a string parameter');
+ }
+
+ options.datepickerInput = datepickerInput;
+ return picker;
+ };
+
+ picker.parseInputDate = function (parseInputDate) {
+ if (arguments.length === 0) {
+ return options.parseInputDate;
+ }
+
+ if (typeof parseInputDate !== 'function') {
+ throw new TypeError('parseInputDate() sholud be as function');
+ }
+
+ options.parseInputDate = parseInputDate;
+
+ return picker;
+ };
+
+ picker.disabledTimeIntervals = function (disabledTimeIntervals) {
+ ///<signature helpKeyword="$.fn.datetimepicker.disabledTimeIntervals">
+ ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
+ ///<returns type="array">options.disabledTimeIntervals</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
+ ///options.enabledDates if such exist.</summary>
+ ///<param name="dates" locid="$.fn.datetimepicker.disabledTimeIntervals_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals);
+ }
+
+ if (!disabledTimeIntervals) {
+ options.disabledTimeIntervals = false;
+ update();
+ return picker;
+ }
+ if (!(disabledTimeIntervals instanceof Array)) {
+ throw new TypeError('disabledTimeIntervals() expects an array parameter');
+ }
+ options.disabledTimeIntervals = disabledTimeIntervals;
+ update();
+ return picker;
+ };
+
+ picker.disabledHours = function (hours) {
+ ///<signature helpKeyword="$.fn.datetimepicker.disabledHours">
+ ///<summary>Returns an array with the currently set disabled hours on the component.</summary>
+ ///<returns type="array">options.disabledHours</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
+ ///options.enabledHours if such exist.</summary>
+ ///<param name="hours" locid="$.fn.datetimepicker.disabledHours_p:hours">Takes an [ int ] of values and disallows the user to select only from those hours.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours);
+ }
+
+ if (!hours) {
+ options.disabledHours = false;
+ update();
+ return picker;
+ }
+ if (!(hours instanceof Array)) {
+ throw new TypeError('disabledHours() expects an array parameter');
+ }
+ options.disabledHours = indexGivenHours(hours);
+ options.enabledHours = false;
+ if (options.useCurrent && !options.keepInvalid) {
+ var tries = 0;
+ while (!isValid(date, 'h')) {
+ date.add(1, 'h');
+ if (tries === 24) {
+ throw 'Tried 24 times to find a valid date';
+ }
+ tries++;
+ }
+ setValue(date);
+ }
+ update();
+ return picker;
+ };
+
+ picker.enabledHours = function (hours) {
+ ///<signature helpKeyword="$.fn.datetimepicker.enabledHours">
+ ///<summary>Returns an array with the currently set enabled hours on the component.</summary>
+ ///<returns type="array">options.enabledHours</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist.</summary>
+ ///<param name="hours" locid="$.fn.datetimepicker.enabledHours_p:hours">Takes an [ int ] of values and allows the user to select only from those hours.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours);
+ }
+
+ if (!hours) {
+ options.enabledHours = false;
+ update();
+ return picker;
+ }
+ if (!(hours instanceof Array)) {
+ throw new TypeError('enabledHours() expects an array parameter');
+ }
+ options.enabledHours = indexGivenHours(hours);
+ options.disabledHours = false;
+ if (options.useCurrent && !options.keepInvalid) {
+ var tries = 0;
+ while (!isValid(date, 'h')) {
+ date.add(1, 'h');
+ if (tries === 24) {
+ throw 'Tried 24 times to find a valid date';
+ }
+ tries++;
+ }
+ setValue(date);
+ }
+ update();
+ return picker;
+ };
+
+ picker.viewDate = function (newDate) {
+ ///<signature helpKeyword="$.fn.datetimepicker.viewDate">
+ ///<summary>Returns the component's model current viewDate, a moment object or null if not set.</summary>
+ ///<returns type="Moment">viewDate.clone()</returns>
+ ///</signature>
+ ///<signature>
+ ///<summary>Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.</summary>
+ ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, viewDate, moment, null parameter.</param>
+ ///</signature>
+ if (arguments.length === 0) {
+ return viewDate.clone();
+ }
+
+ if (!newDate) {
+ viewDate = date.clone();
+ return picker;
+ }
+
+ if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
+ throw new TypeError('viewDate() parameter must be one of [string, moment or Date]');
+ }
+
+ viewDate = parseInputDate(newDate);
+ viewUpdate();
+ return picker;
+ };
+
+ // initializing element and component attributes
+ if (element.is('input')) {
+ input = element;
+ } else {
+ input = element.find(options.datepickerInput);
+ if (input.size() === 0) {
+ input = element.find('input');
+ } else if (!input.is('input')) {
+ throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element');
+ }
+ }
+
+ if (element.hasClass('input-group')) {
+ // in case there is more then one 'input-group-addon' Issue #48
+ if (element.find('.datepickerbutton').size() === 0) {
+ component = element.find('.input-group-addon');
+ } else {
+ component = element.find('.datepickerbutton');
+ }
+ }
+
+ if (!options.inline && !input.is('input')) {
+ throw new Error('Could not initialize DateTimePicker without an input element');
+ }
+
+ // Set defaults for date here now instead of in var declaration
+ date = getMoment();
+ viewDate = date.clone();
+
+ $.extend(true, options, dataToOptions());
+
+ picker.options(options);
+
+ initFormatting();
+
+ attachDatePickerElementEvents();
+
+ if (input.prop('disabled')) {
+ picker.disable();
+ }
+ if (input.is('input') && input.val().trim().length !== 0) {
+ setValue(parseInputDate(input.val().trim()));
+ }
+ else if (options.defaultDate && input.attr('placeholder') === undefined) {
+ setValue(options.defaultDate);
+ }
+ if (options.inline) {
+ show();
+ }
+ return picker;
+ };
+
+ /********************************************************************************
+ *
+ * jQuery plugin constructor and defaults object
+ *
+ ********************************************************************************/
+
+ $.fn.datetimepicker = function (options) {
+ return this.each(function () {
+ var $this = $(this);
+ if (!$this.data('DateTimePicker')) {
+ // create a private copy of the defaults object
+ options = $.extend(true, {}, $.fn.datetimepicker.defaults, options);
+ $this.data('DateTimePicker', dateTimePicker($this, options));
+ }
+ });
+ };
+
+ $.fn.datetimepicker.defaults = {
+ timeZone: 'Etc/UTC',
+ format: false,
+ dayViewHeaderFormat: 'MMMM YYYY',
+ extraFormats: false,
+ stepping: 1,
+ minDate: false,
+ maxDate: false,
+ useCurrent: true,
+ collapse: true,
+ locale: moment.locale(),
+ defaultDate: false,
+ disabledDates: false,
+ enabledDates: false,
+ icons: {
+ time: 'glyphicon glyphicon-time',
+ date: 'glyphicon glyphicon-calendar',
+ up: 'glyphicon glyphicon-chevron-up',
+ down: 'glyphicon glyphicon-chevron-down',
+ previous: 'glyphicon glyphicon-chevron-left',
+ next: 'glyphicon glyphicon-chevron-right',
+ today: 'glyphicon glyphicon-screenshot',
+ clear: 'glyphicon glyphicon-trash',
+ close: 'glyphicon glyphicon-remove'
+ },
+ tooltips: {
+ today: 'Go to today',
+ clear: 'Clear selection',
+ close: 'Close the picker',
+ selectMonth: 'Select Month',
+ prevMonth: 'Previous Month',
+ nextMonth: 'Next Month',
+ selectYear: 'Select Year',
+ prevYear: 'Previous Year',
+ nextYear: 'Next Year',
+ selectDecade: 'Select Decade',
+ prevDecade: 'Previous Decade',
+ nextDecade: 'Next Decade',
+ prevCentury: 'Previous Century',
+ nextCentury: 'Next Century',
+ pickHour: 'Pick Hour',
+ incrementHour: 'Increment Hour',
+ decrementHour: 'Decrement Hour',
+ pickMinute: 'Pick Minute',
+ incrementMinute: 'Increment Minute',
+ decrementMinute: 'Decrement Minute',
+ pickSecond: 'Pick Second',
+ incrementSecond: 'Increment Second',
+ decrementSecond: 'Decrement Second',
+ togglePeriod: 'Toggle Period',
+ selectTime: 'Select Time'
+ },
+ useStrict: false,
+ sideBySide: false,
+ daysOfWeekDisabled: false,
+ calendarWeeks: false,
+ viewMode: 'days',
+ toolbarPlacement: 'default',
+ showTodayButton: false,
+ showClear: false,
+ showClose: false,
+ widgetPositioning: {
+ horizontal: 'auto',
+ vertical: 'auto'
+ },
+ widgetParent: null,
+ ignoreReadonly: false,
+ keepOpen: false,
+ focusOnShow: true,
+ inline: false,
+ keepInvalid: false,
+ datepickerInput: '.datepickerinput',
+ keyBinds: {
+ up: function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().subtract(7, 'd'));
+ } else {
+ this.date(d.clone().add(this.stepping(), 'm'));
+ }
+ },
+ down: function (widget) {
+ if (!widget) {
+ this.show();
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().add(7, 'd'));
+ } else {
+ this.date(d.clone().subtract(this.stepping(), 'm'));
+ }
+ },
+ 'control up': function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().subtract(1, 'y'));
+ } else {
+ this.date(d.clone().add(1, 'h'));
+ }
+ },
+ 'control down': function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().add(1, 'y'));
+ } else {
+ this.date(d.clone().subtract(1, 'h'));
+ }
+ },
+ left: function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().subtract(1, 'd'));
+ }
+ },
+ right: function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().add(1, 'd'));
+ }
+ },
+ pageUp: function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().subtract(1, 'M'));
+ }
+ },
+ pageDown: function (widget) {
+ if (!widget) {
+ return;
+ }
+ var d = this.date() || this.getMoment();
+ if (widget.find('.datepicker').is(':visible')) {
+ this.date(d.clone().add(1, 'M'));
+ }
+ },
+ enter: function () {
+ this.hide();
+ },
+ escape: function () {
+ this.hide();
+ },
+ //tab: function (widget) { //this break the flow of the form. disabling for now
+ // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]');
+ // if(toggle.length > 0) toggle.click();
+ //},
+ 'control space': function (widget) {
+ if (widget.find('.timepicker').is(':visible')) {
+ widget.find('.btn[data-action="togglePeriod"]').click();
+ }
+ },
+ t: function () {
+ this.date(this.getMoment());
+ },
+ 'delete': function () {
+ this.clear();
+ }
+ },
+ debug: false,
+ allowInputToggle: false,
+ disabledTimeIntervals: false,
+ disabledHours: false,
+ enabledHours: false,
+ viewDate: false
+ };
+}));
diff --git a/src/less/_bootstrap-datetimepicker.less b/src/less/_bootstrap-datetimepicker.less
new file mode 100644
index 0000000..5beb293
--- /dev/null
+++ b/src/less/_bootstrap-datetimepicker.less
@@ -0,0 +1,352 @@
+/*!
+ * Datetimepicker for Bootstrap 3
+ * version : 4.17.37
+ * https://github.com/Eonasdan/bootstrap-datetimepicker/
+ */
+ at bs-datetimepicker-timepicker-font-size: 1.2em;
+ at bs-datetimepicker-active-bg: @btn-primary-bg;
+ at bs-datetimepicker-active-color: @btn-primary-color;
+ at bs-datetimepicker-border-radius: @border-radius-base;
+ at bs-datetimepicker-btn-hover-bg: @gray-lighter;
+ at bs-datetimepicker-disabled-color: @gray-light;
+ at bs-datetimepicker-alternate-color: @gray-light;
+ at bs-datetimepicker-secondary-border-color: #ccc;
+ at bs-datetimepicker-secondary-border-color-rgba: rgba(0, 0, 0, 0.2);
+ at bs-datetimepicker-primary-border-color: white;
+ at bs-datetimepicker-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+
+.bootstrap-datetimepicker-widget {
+ list-style: none;
+
+ &.dropdown-menu {
+ margin: 2px 0;
+ padding: 4px;
+ width: 19em;
+
+ &.timepicker-sbs {
+ @media (min-width: @screen-sm-min) {
+ width: 38em;
+ }
+
+ @media (min-width: @screen-md-min) {
+ width: 38em;
+ }
+
+ @media (min-width: @screen-lg-min) {
+ width: 38em;
+ }
+ }
+
+ &:before, &:after {
+ content: '';
+ display: inline-block;
+ position: absolute;
+ }
+
+ &.bottom {
+ &:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid @bs-datetimepicker-secondary-border-color;
+ border-bottom-color: @bs-datetimepicker-secondary-border-color-rgba;
+ top: -7px;
+ left: 7px;
+ }
+
+ &:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid @bs-datetimepicker-primary-border-color;
+ top: -6px;
+ left: 8px;
+ }
+ }
+
+ &.top {
+ &:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-top: 7px solid @bs-datetimepicker-secondary-border-color;
+ border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
+ bottom: -7px;
+ left: 6px;
+ }
+
+ &:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid @bs-datetimepicker-primary-border-color;
+ bottom: -6px;
+ left: 7px;
+ }
+ }
+
+ &.pull-right {
+ &:before {
+ left: auto;
+ right: 6px;
+ }
+
+ &:after {
+ left: auto;
+ right: 7px;
+ }
+ }
+ }
+
+ .list-unstyled {
+ margin: 0;
+ }
+
+ a[data-action] {
+ padding: 6px 0;
+ }
+
+ a[data-action]:active {
+ box-shadow: none;
+ }
+
+ .timepicker-hour, .timepicker-minute, .timepicker-second {
+ width: 54px;
+ font-weight: bold;
+ font-size: @bs-datetimepicker-timepicker-font-size;
+ margin: 0;
+ }
+
+ button[data-action] {
+ padding: 6px;
+ }
+
+ .btn[data-action="incrementHours"]::after {
+ .sr-only();
+ content: "Increment Hours";
+ }
+
+ .btn[data-action="incrementMinutes"]::after {
+ .sr-only();
+ content: "Increment Minutes";
+ }
+
+ .btn[data-action="decrementHours"]::after {
+ .sr-only();
+ content: "Decrement Hours";
+ }
+
+ .btn[data-action="decrementMinutes"]::after {
+ .sr-only();
+ content: "Decrement Minutes";
+ }
+
+ .btn[data-action="showHours"]::after {
+ .sr-only();
+ content: "Show Hours";
+ }
+
+ .btn[data-action="showMinutes"]::after {
+ .sr-only();
+ content: "Show Minutes";
+ }
+
+ .btn[data-action="togglePeriod"]::after {
+ .sr-only();
+ content: "Toggle AM/PM";
+ }
+
+ .btn[data-action="clear"]::after {
+ .sr-only();
+ content: "Clear the picker";
+ }
+
+ .btn[data-action="today"]::after {
+ .sr-only();
+ content: "Set the date to today";
+ }
+
+ .picker-switch {
+ text-align: center;
+
+ &::after {
+ .sr-only();
+ content: "Toggle Date and Time Screens";
+ }
+
+ td {
+ padding: 0;
+ margin: 0;
+ height: auto;
+ width: auto;
+ line-height: inherit;
+
+ span {
+ line-height: 2.5;
+ height: 2.5em;
+ width: 100%;
+ }
+ }
+ }
+
+ table {
+ width: 100%;
+ margin: 0;
+
+
+ & td,
+ & th {
+ text-align: center;
+ border-radius: @bs-datetimepicker-border-radius;
+ }
+
+ & th {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+
+ &.picker-switch {
+ width: 145px;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: @bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+
+ &.prev::after {
+ .sr-only();
+ content: "Previous Month";
+ }
+
+ &.next::after {
+ .sr-only();
+ content: "Next Month";
+ }
+ }
+
+ & thead tr:first-child th {
+ cursor: pointer;
+
+ &:hover {
+ background: @bs-datetimepicker-btn-hover-bg;
+ }
+ }
+
+ & td {
+ height: 54px;
+ line-height: 54px;
+ width: 54px;
+
+ &.cw {
+ font-size: .8em;
+ height: 20px;
+ line-height: 20px;
+ color: @bs-datetimepicker-alternate-color;
+ }
+
+ &.day {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+ }
+
+ &.day:hover,
+ &.hour:hover,
+ &.minute:hover,
+ &.second:hover {
+ background: @bs-datetimepicker-btn-hover-bg;
+ cursor: pointer;
+ }
+
+ &.old,
+ &.new {
+ color: @bs-datetimepicker-alternate-color;
+ }
+
+ &.today {
+ position: relative;
+
+ &:before {
+ content: '';
+ display: inline-block;
+ border: solid transparent;
+ border-width: 0 0 7px 7px;
+ border-bottom-color: @bs-datetimepicker-active-bg;
+ border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
+ position: absolute;
+ bottom: 4px;
+ right: 4px;
+ }
+ }
+
+ &.active,
+ &.active:hover {
+ background-color: @bs-datetimepicker-active-bg;
+ color: @bs-datetimepicker-active-color;
+ text-shadow: @bs-datetimepicker-text-shadow;
+ }
+
+ &.active.today:before {
+ border-bottom-color: #fff;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: @bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+
+ span {
+ display: inline-block;
+ width: 54px;
+ height: 54px;
+ line-height: 54px;
+ margin: 2px 1.5px;
+ cursor: pointer;
+ border-radius: @bs-datetimepicker-border-radius;
+
+ &:hover {
+ background: @bs-datetimepicker-btn-hover-bg;
+ }
+
+ &.active {
+ background-color: @bs-datetimepicker-active-bg;
+ color: @bs-datetimepicker-active-color;
+ text-shadow: @bs-datetimepicker-text-shadow;
+ }
+
+ &.old {
+ color: @bs-datetimepicker-alternate-color;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: @bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+ }
+ }
+ }
+
+ &.usetwentyfour {
+ td.hour {
+ height: 27px;
+ line-height: 27px;
+ }
+ }
+
+ &.wider {
+ width: 21em;
+ }
+
+ & .datepicker-decades .decade {
+ line-height: 1.8em !important;
+ }
+}
+
+.input-group.date {
+ & .input-group-addon {
+ cursor: pointer;
+ }
+}
diff --git a/src/less/bootstrap-datetimepicker-build.less b/src/less/bootstrap-datetimepicker-build.less
new file mode 100644
index 0000000..3f0b188
--- /dev/null
+++ b/src/less/bootstrap-datetimepicker-build.less
@@ -0,0 +1,17 @@
+// Import bootstrap variables including default color palette and fonts
+ at import "bootstrap/less/variables.less";
+
+// Import datepicker component
+ at import "_bootstrap-datetimepicker.less";
+
+//this is here so the compiler doesn't complain about a missing bootstrap mixin
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ border: 0;
+}
diff --git a/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec b/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec
new file mode 100644
index 0000000..da03408
--- /dev/null
+++ b/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
+ <metadata>
+ <id>Bootstrap.v3.Datetimepicker.CSS</id>
+ <version>4.0.0</version>
+ <title>Bootstrap 3 Datetimepicker CSS</title>
+ <authors>Eonasdan</authors>
+ <owners>Eonasdan</owners>
+ <projectUrl>https://github.com/Eonasdan/bootstrap-datetimepicker</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>A date/time picker component designed to work with Bootstrap 3 and Momentjs.
+
+For usage, installation and demos see Project Site on GitHub
+
+For LESS version install Bootstrap.v3.Datetimepicker</description>
+ <releaseNotes>
+ Check the change log on Github at https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log
+
+ IMPORANT! The Nuget packages will be depreciated in an upcoming release. Moving forward, Asp.Net/Nuget will NOT be delivering content packages like this one and you will need to use bower. See https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1128 for more
+ </releaseNotes>
+ <tags>bootstrap date time picker datetimepicker datepicker jquery</tags>
+ <dependencies>
+ <dependency id="bootstrap" version="3.3.0" />
+ <dependency id="Moment.js" version="2.9.0" />
+ </dependencies>
+ </metadata>
+ <files>
+ <file src="..\..\src\js\bootstrap-datetimepicker.js" target="content\Scripts" />
+ <file src="..\..\build\js\bootstrap-datetimepicker.min.js" target="content\Scripts" />
+ <file src="..\..\build\css\bootstrap-datetimepicker.css" target="content\Content" />
+ <file src="..\..\build\css\bootstrap-datetimepicker.min.css" target="content\Content" />
+ <file src="install.ps1" target="tools\" />
+ </files>
+</package>
\ No newline at end of file
diff --git a/src/nuget/Bootstrap.v3.Datetimepicker.nuspec b/src/nuget/Bootstrap.v3.Datetimepicker.nuspec
new file mode 100644
index 0000000..8678e0a
--- /dev/null
+++ b/src/nuget/Bootstrap.v3.Datetimepicker.nuspec
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
+ <metadata>
+ <id>Bootstrap.v3.Datetimepicker</id>
+ <version>4.0.0</version>
+ <title>Bootstrap 3 Datetimepicker</title>
+ <authors>Eonasdan</authors>
+ <owners>Eonasdan</owners>
+ <projectUrl>https://github.com/Eonasdan/bootstrap-datetimepicker</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>A date/time picker component designed to work with Bootstrap 3 and Momentjs.
+
+For usage, installation and demos see Project Site on GitHub
+
+For CSS version install Bootstrap.v3.Datetimepicker.CSS</description>
+ <releaseNotes>
+ Check the change log on Github at https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log
+
+ IMPORANT! The Nuget packages will be depreciated in an upcoming release. Moving forward, Asp.Net/Nuget will NOT be delivering content packages like this one and you will need to use bower. See https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1128 for more
+ </releaseNotes>
+ <tags>bootstrap date time picker datetimepicker datepicker jquery</tags>
+ <dependencies>
+ <dependency id="bootstrap.Less" version="3.3.0" />
+ <dependency id="Moment.js" version="2.9.0" />
+ </dependencies>
+ </metadata>
+ <files>
+ <file src="..\..\src\js\bootstrap-datetimepicker.js" target="content\Scripts" />
+ <file src="..\..\build\js\bootstrap-datetimepicker.min.js" target="content\Scripts" />
+ <file src="..\..\src\less\_bootstrap-datetimepicker.less" target="content\Content\" />
+ <file src="..\..\src\less\bootstrap-datetimepicker-build.less" target="content\Content\" />
+ <file src="install.ps1" target="tools\" />
+ </files>
+</package>
\ No newline at end of file
diff --git a/src/nuget/NuGet.exe b/src/nuget/NuGet.exe
new file mode 100644
index 0000000..c41a0d0
Binary files /dev/null and b/src/nuget/NuGet.exe differ
diff --git a/src/nuget/install.ps1 b/src/nuget/install.ps1
new file mode 100644
index 0000000..3b0e753
--- /dev/null
+++ b/src/nuget/install.ps1
@@ -0,0 +1,2 @@
+# install.ps1
+$DTE.ItemOperations.Navigate("https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1128", $DTE.vsNavigateOptions.vsNavigateOptionsNewWindow)
diff --git a/src/sass/_bootstrap-datetimepicker.scss b/src/sass/_bootstrap-datetimepicker.scss
new file mode 100644
index 0000000..06489be
--- /dev/null
+++ b/src/sass/_bootstrap-datetimepicker.scss
@@ -0,0 +1,343 @@
+/*!
+ * Datetimepicker for Bootstrap 3
+ * ! version : 4.7.14
+ * https://github.com/Eonasdan/bootstrap-datetimepicker/
+ */
+$bs-datetimepicker-timepicker-font-size: 1.2em !default;
+$bs-datetimepicker-active-bg: $btn-primary-bg !default;
+$bs-datetimepicker-active-color: $btn-primary-color !default;
+$bs-datetimepicker-border-radius: $border-radius-base !default;
+$bs-datetimepicker-btn-hover-bg: $gray-lighter !default;
+$bs-datetimepicker-disabled-color: $gray-light !default;
+$bs-datetimepicker-alternate-color: $gray-light !default;
+$bs-datetimepicker-secondary-border-color: #ccc !default;
+$bs-datetimepicker-secondary-border-color-rgba: rgba(0, 0, 0, 0.2) !default;
+$bs-datetimepicker-primary-border-color: white !default;
+$bs-datetimepicker-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25) !default;
+
+.bootstrap-datetimepicker-widget {
+ list-style: none;
+
+ &.dropdown-menu {
+ margin: 2px 0;
+ padding: 4px;
+ width: 19em;
+
+ &.timepicker-sbs {
+ @media (min-width: $screen-sm-min) {
+ width: 38em;
+ }
+
+ @media (min-width: $screen-md-min) {
+ width: 38em;
+ }
+
+ @media (min-width: $screen-lg-min) {
+ width: 38em;
+ }
+ }
+
+ &:before, &:after {
+ content: '';
+ display: inline-block;
+ position: absolute;
+ }
+
+ &.bottom {
+ &:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid $bs-datetimepicker-secondary-border-color;
+ border-bottom-color: $bs-datetimepicker-secondary-border-color-rgba;
+ top: -7px;
+ left: 7px;
+ }
+
+ &:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid $bs-datetimepicker-primary-border-color;
+ top: -6px;
+ left: 8px;
+ }
+ }
+
+ &.top {
+ &:before {
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-top: 7px solid $bs-datetimepicker-secondary-border-color;
+ border-top-color: $bs-datetimepicker-secondary-border-color-rgba;
+ bottom: -7px;
+ left: 6px;
+ }
+
+ &:after {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid $bs-datetimepicker-primary-border-color;
+ bottom: -6px;
+ left: 7px;
+ }
+ }
+
+ &.pull-right {
+ &:before {
+ left: auto;
+ right: 6px;
+ }
+
+ &:after {
+ left: auto;
+ right: 7px;
+ }
+ }
+ }
+
+ .list-unstyled {
+ margin: 0;
+ }
+
+ a[data-action] {
+ padding: 6px 0;
+ }
+
+ a[data-action]:active {
+ box-shadow: none;
+ }
+
+ .timepicker-hour, .timepicker-minute, .timepicker-second {
+ width: 54px;
+ font-weight: bold;
+ font-size: $bs-datetimepicker-timepicker-font-size;
+ margin: 0;
+ }
+
+ button[data-action] {
+ padding: 6px;
+ }
+
+ .btn[data-action="incrementHours"]::after {
+ @extend .sr-only;
+ content: "Increment Hours";
+ }
+
+ .btn[data-action="incrementMinutes"]::after {
+ @extend .sr-only;
+ content: "Increment Minutes";
+ }
+
+ .btn[data-action="decrementHours"]::after {
+ @extend .sr-only;
+ content: "Decrement Hours";
+ }
+
+ .btn[data-action="decrementMinutes"]::after {
+ @extend .sr-only;
+ content: "Decrement Minutes";
+ }
+
+ .btn[data-action="showHours"]::after {
+ @extend .sr-only;
+ content: "Show Hours";
+ }
+
+ .btn[data-action="showMinutes"]::after {
+ @extend .sr-only;
+ content: "Show Minutes";
+ }
+
+ .btn[data-action="togglePeriod"]::after {
+ @extend .sr-only;
+ content: "Toggle AM/PM";
+ }
+
+ .btn[data-action="clear"]::after {
+ @extend .sr-only;
+ content: "Clear the picker";
+ }
+
+ .btn[data-action="today"]::after {
+ @extend .sr-only;
+ content: "Set the date to today";
+ }
+
+ .picker-switch {
+ text-align: center;
+
+ &::after {
+ @extend .sr-only;
+ content: "Toggle Date and Time Screens";
+ }
+
+ td {
+ padding: 0;
+ margin: 0;
+ height: auto;
+ width: auto;
+ line-height: inherit;
+
+ span {
+ line-height: 2.5;
+ height: 2.5em;
+ width: 100%;
+ }
+ }
+ }
+
+ table {
+ width: 100%;
+ margin: 0;
+
+
+ & td,
+ & th {
+ text-align: center;
+ border-radius: $bs-datetimepicker-border-radius;
+ }
+
+ & th {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+
+ &.picker-switch {
+ width: 145px;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: $bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+
+ &.prev::after {
+ @extend .sr-only;
+ content: "Previous Month";
+ }
+
+ &.next::after {
+ @extend .sr-only;
+ content: "Next Month";
+ }
+ }
+
+ & thead tr:first-child th {
+ cursor: pointer;
+
+ &:hover {
+ background: $bs-datetimepicker-btn-hover-bg;
+ }
+ }
+
+ & td {
+ height: 54px;
+ line-height: 54px;
+ width: 54px;
+
+ &.cw {
+ font-size: .8em;
+ height: 20px;
+ line-height: 20px;
+ color: $bs-datetimepicker-alternate-color;
+ }
+
+ &.day {
+ height: 20px;
+ line-height: 20px;
+ width: 20px;
+ }
+
+ &.day:hover,
+ &.hour:hover,
+ &.minute:hover,
+ &.second:hover {
+ background: $bs-datetimepicker-btn-hover-bg;
+ cursor: pointer;
+ }
+
+ &.old,
+ &.new {
+ color: $bs-datetimepicker-alternate-color;
+ }
+
+ &.today {
+ position: relative;
+
+ &:before {
+ content: '';
+ display: inline-block;
+ border: 0 0 7px 7px solid transparent;
+ border-bottom-color: $bs-datetimepicker-active-bg;
+ border-top-color: $bs-datetimepicker-secondary-border-color-rgba;
+ position: absolute;
+ bottom: 4px;
+ right: 4px;
+ }
+ }
+
+ &.active,
+ &.active:hover {
+ background-color: $bs-datetimepicker-active-bg;
+ color: $bs-datetimepicker-active-color;
+ text-shadow: $bs-datetimepicker-text-shadow;
+ }
+
+ &.active.today:before {
+ border-bottom-color: #fff;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: $bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+
+ span {
+ display: inline-block;
+ width: 54px;
+ height: 54px;
+ line-height: 54px;
+ margin: 2px 1.5px;
+ cursor: pointer;
+ border-radius: $bs-datetimepicker-border-radius;
+
+ &:hover {
+ background: $bs-datetimepicker-btn-hover-bg;
+ }
+
+ &.active {
+ background-color: $bs-datetimepicker-active-bg;
+ color: $bs-datetimepicker-active-color;
+ text-shadow: $bs-datetimepicker-text-shadow;
+ }
+
+ &.old {
+ color: $bs-datetimepicker-alternate-color;
+ }
+
+ &.disabled,
+ &.disabled:hover {
+ background: none;
+ color: $bs-datetimepicker-disabled-color;
+ cursor: not-allowed;
+ }
+ }
+ }
+ }
+
+ &.usetwentyfour {
+ td.hour {
+ height: 27px;
+ line-height: 27px;
+ }
+ }
+}
+
+.input-group.date {
+ & .input-group-addon {
+ cursor: pointer;
+ }
+}
\ No newline at end of file
diff --git a/src/sass/bootstrap-datetimepicker-build.scss b/src/sass/bootstrap-datetimepicker-build.scss
new file mode 100644
index 0000000..d578615
--- /dev/null
+++ b/src/sass/bootstrap-datetimepicker-build.scss
@@ -0,0 +1,16 @@
+// Import bootstrap variables including default color palette and fonts
+//@import "../../node_modules/bootstrap/less/variables.less";
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ border: 0;
+}
+
+// Import datepicker component
+ at import "_bootstrap-datetimepicker";
diff --git a/tasks/bump_version.js b/tasks/bump_version.js
new file mode 100644
index 0000000..76552ca
--- /dev/null
+++ b/tasks/bump_version.js
@@ -0,0 +1,88 @@
+module.exports = function (grunt) {
+ grunt.registerTask('bump_version', function (version) {
+ if (!version || version.split('.').length !== 3) {
+ grunt.fail.fatal('malformed version. Use\n\n grunt bump_version:1.2.3');
+ }
+
+ grunt.config('string-replace.bootstrap-datetimepicker-js', {
+ files: {'src/js/bootstrap-datetimepicker.js': 'src/js/bootstrap-datetimepicker.js'},
+ options: {
+ replacements: [
+ {
+ pattern: /\/*! version : .*/,
+ replacement: '! version : ' + version
+ }
+ ]
+ }
+ });
+
+ grunt.config('string-replace.bootstrap-datetimepicker-css', {
+ files: { 'src/less/_bootstrap-datetimepicker.less': 'src/less/_bootstrap-datetimepicker.less' },
+ options: {
+ replacements: [
+ {
+ pattern: / * version : .*/,
+ replacement: ' version : ' + version
+ }
+ ]
+ }
+ });
+
+ grunt.config('string-replace.package-json', {
+ files: {'package.json': 'package.json'},
+ options: {
+ replacements: [
+ {
+ pattern: /"version": .*/,
+ replacement: '"version": "' + version + '"'
+ }
+ ]
+ }
+ });
+
+ grunt.config('string-replace.bower-json', {
+ files: {'bower.json': 'bower.json'},
+ options: {
+ replacements: [
+ {
+ pattern: /"version": .*/,
+ replacement: '"version": "' + version + '",'
+ }
+ ]
+ }
+ });
+
+ grunt.config('string-replace.component-json', {
+ files: {'component.json': 'component.json'},
+ options: {
+ replacements: [
+ {
+ pattern: /"version": .*/,
+ replacement: '"version": "' + version + '",'
+ }
+ ]
+ }
+ });
+
+ grunt.config('string-replace.composer-json', {
+ files: {'composer.json': 'composer.json'},
+ options: {
+ replacements: [
+ {
+ pattern: /"version": .*/,
+ replacement: '"version": "' + version + '",'
+ }
+ ]
+ }
+ });
+
+ grunt.task.run([
+ 'string-replace:bootstrap-datetimepicker-js',
+ 'string-replace:bootstrap-datetimepicker-css',
+ 'string-replace:package-json',
+ 'string-replace:bower-json',
+ 'string-replace:component-json',
+ 'string-replace:composer-json'
+ ]);
+ });
+};
diff --git a/test/publicApiSpec.js b/test/publicApiSpec.js
new file mode 100644
index 0000000..7812cb8
--- /dev/null
+++ b/test/publicApiSpec.js
@@ -0,0 +1,708 @@
+describe('Plugin initialization and component basic construction', function () {
+ 'use strict';
+
+ it('loads jquery plugin properly', function () {
+ expect($('<div>').datetimepicker).toBeDefined();
+ expect(typeof $('<div>').datetimepicker).toEqual('function');
+ expect($('<div>').datetimepicker.defaults).toBeDefined();
+ });
+
+ it('creates the component with default options on an input element', function () {
+ var dtp = $('<input>');
+ $(document).find('body').append(dtp);
+
+ expect(function () {
+ dtp = dtp.datetimepicker();
+ }).not.toThrow();
+
+ expect(dtp).not.toBe(null);
+ });
+
+ xit('calls destroy when Element that the component is attached is removed', function () {
+ var dtpElement = $('<div>').attr('class', 'row').append($('<div>').attr('class', 'col-md-12').append($('<input>'))),
+ dtp;
+ $(document).find('body').append(dtpElement);
+ dtpElement.datetimepicker();
+ dtp = dtpElement.data('DateTimePicker');
+ spyOn(dtp, 'destroy').and.callThrough();
+ dtpElement.remove();
+ expect(dtp.destroy).toHaveBeenCalled();
+ });
+});
+
+describe('Public API method tests', function () {
+ 'use strict';
+ var dtp,
+ dtpElement,
+ dpChangeSpy,
+ dpShowSpy,
+ dpHideSpy,
+ dpErrorSpy;
+
+ beforeEach(function () {
+ dpChangeSpy = jasmine.createSpy('dp.change event Spy');
+ dpShowSpy = jasmine.createSpy('dp.show event Spy');
+ dpHideSpy = jasmine.createSpy('dp.hide event Spy');
+ dpErrorSpy = jasmine.createSpy('dp.error event Spy');
+ dtpElement = $('<input>').attr('id', 'dtp');
+
+ $(document).find('body').append($('<div>').attr('class', 'row').append($('<div>').attr('class', 'col-md-12').append(dtpElement)));
+ $(document).find('body').on('dp.change', dpChangeSpy);
+ $(document).find('body').on('dp.show', dpShowSpy);
+ $(document).find('body').on('dp.hide', dpHideSpy);
+ $(document).find('body').on('dp.error', dpErrorSpy);
+
+ dtpElement.datetimepicker();
+ dtp = dtpElement.data('DateTimePicker');
+ });
+
+ afterEach(function () {
+ dtp.destroy();
+ dtpElement.remove();
+ });
+
+ describe('configuration option name match to public api function', function () {
+ Object.getOwnPropertyNames($.fn.datetimepicker.defaults).forEach(function (key) {
+ it('has function ' + key + '()', function () {
+ expect(dtp[key]).toBeDefined();
+ });
+ });
+ });
+
+ describe('date() function', function () {
+ describe('typechecking', function () {
+ it('accepts a null', function () {
+ expect(function () {
+ dtp.date(null);
+ }).not.toThrow();
+ });
+
+ it('accepts a string', function () {
+ expect(function () {
+ dtp.date('2013/05/24');
+ }).not.toThrow();
+ });
+
+ it('accepts a Date object', function () {
+ expect(function () {
+ dtp.date(new Date());
+ }).not.toThrow();
+ });
+
+ it('accepts a Moment object', function () {
+ expect(function () {
+ dtp.date(moment());
+ }).not.toThrow();
+ });
+
+ it('does not accept undefined', function () {
+ expect(function () {
+ dtp.date(undefined);
+ }).toThrow();
+ });
+
+ it('does not accept a number', function () {
+ expect(function () {
+ dtp.date(0);
+ }).toThrow();
+ });
+
+ it('does not accept a generic Object', function () {
+ expect(function () {
+ dtp.date({});
+ }).toThrow();
+ });
+
+ it('does not accept a boolean', function () {
+ expect(function () {
+ dtp.date(false);
+ }).toThrow();
+ });
+ });
+
+ describe('functionality', function () {
+ it('has no date set upon construction', function () {
+ expect(dtp.date()).toBe(null);
+ });
+
+ it('sets the date correctly', function () {
+ var timestamp = moment();
+ dtp.date(timestamp);
+ expect(dtp.date().isSame(timestamp)).toBe(true);
+ });
+ });
+ });
+
+ describe('format() function', function () {
+ describe('typechecking', function () {
+ it('accepts a false value', function () {
+ expect(function () {
+ dtp.format(false);
+ }).not.toThrow();
+ });
+
+ it('accepts a string', function () {
+ expect(function () {
+ dtp.format('YYYY-MM-DD');
+ }).not.toThrow();
+ });
+
+ it('does not accept undefined', function () {
+ expect(function () {
+ dtp.format(undefined);
+ }).toThrow();
+ });
+
+ it('does not accept true', function () {
+ expect(function () {
+ dtp.format(true);
+ }).toThrow();
+ });
+
+ it('does not accept a generic Object', function () {
+ expect(function () {
+ dtp.format({});
+ }).toThrow();
+ });
+ });
+
+ describe('functionality', function () {
+ it('returns no format before format is set', function () {
+ expect(dtp.format()).toBe(false);
+ });
+
+ it('sets the format correctly', function () {
+ dtp.format('YYYY-MM-DD');
+ expect(dtp.format()).toBe('YYYY-MM-DD');
+ });
+ });
+ });
+
+ describe('destroy() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.destroy).toBeDefined();
+ });
+ });
+ });
+
+ describe('toggle() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.toggle).toBeDefined();
+ });
+ });
+
+ // describe('functionality', function () {
+ // it('')
+ // });
+ });
+
+ describe('show() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.show).toBeDefined();
+ });
+ });
+
+ describe('functionality', function () {
+ it('emits a show event when called while widget is hidden', function () {
+ dtp.show();
+ expect(dpShowSpy).toHaveBeenCalled();
+ });
+
+ it('does not emit a show event when called and widget is already showing', function () {
+ dtp.hide();
+ dtp.show();
+ dpShowSpy.calls.reset();
+ dtp.show();
+ expect(dpShowSpy).not.toHaveBeenCalled();
+ });
+
+ it('actually shows the widget', function () {
+ dtp.show();
+ expect($(document).find('body').find('.bootstrap-datetimepicker-widget').length).toEqual(1);
+ });
+ });
+ });
+
+ describe('hide() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.hide).toBeDefined();
+ });
+ });
+
+ describe('functionality', function () {
+ it('emits a hide event when called while widget is shown', function () {
+ dtp.show();
+ dtp.hide();
+ expect(dpHideSpy).toHaveBeenCalled();
+ });
+
+ it('does not emit a hide event when called while widget is hidden', function () {
+ dtp.hide();
+ expect(dpHideSpy).not.toHaveBeenCalled();
+ });
+
+ it('actually hides the widget', function () {
+ dtp.show();
+ dtp.hide();
+ expect($(document).find('body').find('.bootstrap-datetimepicker-widget').length).toEqual(0);
+ });
+ });
+ });
+
+ describe('disable() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.disable).toBeDefined();
+ });
+ });
+ });
+
+ describe('enable() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.enable).toBeDefined();
+ });
+ });
+ });
+
+ describe('options() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.options).toBeDefined();
+ });
+ });
+ });
+
+ describe('disabledDates() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.disabledDates).toBeDefined();
+ });
+ });
+ });
+
+ describe('enabledDates() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.enabledDates).toBeDefined();
+ });
+ });
+ });
+
+ describe('daysOfWeekDisabled() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.daysOfWeekDisabled).toBeDefined();
+ });
+ });
+ });
+
+ describe('maxDate() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.maxDate).toBeDefined();
+ });
+ });
+ });
+
+ describe('minDate() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.minDate).toBeDefined();
+ });
+ });
+ });
+
+ describe('defaultDate() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.defaultDate).toBeDefined();
+ });
+ });
+ describe('functionality', function () {
+ it('returns no defaultDate before defaultDate is set', function () {
+ expect(dtp.defaultDate()).toBe(false);
+ });
+
+ it('sets the defaultDate correctly', function () {
+ var timestamp = moment();
+ dtp.defaultDate(timestamp);
+ expect(dtp.defaultDate().isSame(timestamp)).toBe(true);
+ expect(dtp.date().isSame(timestamp)).toBe(true);
+ });
+
+ it('triggers a change event upon setting a default date and input field is empty', function () {
+ dtp.date(null);
+ dtp.defaultDate(moment());
+ expect(dpChangeSpy).toHaveBeenCalled();
+ });
+
+ it('does not override input value if it already has one', function () {
+ var timestamp = moment();
+ dtp.date(timestamp);
+ dtp.defaultDate(moment().year(2000));
+ expect(dtp.date().isSame(timestamp)).toBe(true);
+ });
+ });
+ });
+
+ describe('locale() function', function () {
+ describe('functionality', function () {
+ it('it has the same locale as the global moment locale with default options', function () {
+ expect(dtp.locale()).toBe(moment.locale());
+ });
+
+ it('it switches to a selected locale without affecting global moment locale', function () {
+ dtp.locale('el');
+ dtp.date(moment());
+ expect(dtp.locale()).toBe('el');
+ expect(dtp.date().locale()).toBe('el');
+ expect(moment.locale()).toBe('en');
+ });
+ });
+ });
+
+ describe('useCurrent() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.useCurrent).toBeDefined();
+ });
+ });
+ describe('check type and parameter validity', function () {
+ it('accepts either a boolean value or string', function () {
+ var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
+
+ expect(function () {
+ dtp.useCurrent(false);
+ }).not.toThrow();
+ expect(function () {
+ dtp.useCurrent(true);
+ }).not.toThrow();
+
+ useCurrentOptions.forEach(function (value) {
+ expect(function () {
+ dtp.useCurrent(value);
+ }).not.toThrow();
+ });
+
+ expect(function () {
+ dtp.useCurrent('test');
+ }).toThrow();
+ expect(function () {
+ dtp.useCurrent({});
+ }).toThrow();
+ });
+ });
+ describe('functionality', function () {
+ it('triggers a change event upon show() and input field is empty', function () {
+ dtp.useCurrent(true);
+ dtp.show();
+ expect(dpChangeSpy).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('ignoreReadonly() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.ignoreReadonly).toBeDefined();
+ });
+ });
+ });
+
+ describe('stepping() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.stepping).toBeDefined();
+ });
+ });
+ });
+
+ describe('collapse() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.collapse).toBeDefined();
+ });
+ });
+ });
+
+ describe('icons() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.icons).toBeDefined();
+ });
+ });
+ });
+
+ describe('useStrict() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.useStrict).toBeDefined();
+ });
+ });
+ });
+
+ describe('sideBySide() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.sideBySide).toBeDefined();
+ });
+ });
+ });
+
+ describe('viewMode() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.viewMode).toBeDefined();
+ });
+ });
+ });
+
+ describe('widgetPositioning() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.widgetPositioning).toBeDefined();
+ });
+ });
+ });
+
+ describe('calendarWeeks() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.calendarWeeks).toBeDefined();
+ });
+ });
+ });
+
+ describe('showTodayButton() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.showTodayButton).toBeDefined();
+ });
+ });
+ });
+
+ describe('showClear() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.showClear).toBeDefined();
+ });
+ });
+ });
+
+ describe('dayViewHeaderFormat() function', function () {
+ describe('typechecking', function () {
+ it('does not accept a false value', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat(false);
+ }).toThrow();
+ });
+
+ it('accepts a string', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat('YYYY-MM-DD');
+ }).not.toThrow();
+ });
+
+ it('does not accept undefined', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat(undefined);
+ }).toThrow();
+ });
+
+ it('does not accept true', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat(true);
+ }).toThrow();
+ });
+
+ it('does not accept a generic Object', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat({});
+ }).toThrow();
+ });
+ });
+
+ describe('functionality', function () {
+ it('expects dayViewHeaderFormat to be default of MMMM YYYY', function () {
+ expect(dtp.dayViewHeaderFormat()).toBe('MMMM YYYY');
+ });
+
+ it('sets the dayViewHeaderFormat correctly', function () {
+ dtp.dayViewHeaderFormat('MM YY');
+ expect(dtp.dayViewHeaderFormat()).toBe('MM YY');
+ });
+ });
+ });
+
+ describe('extraFormats() function', function () {
+ describe('typechecking', function () {
+ it('accepts a false value', function () {
+ expect(function () {
+ dtp.extraFormats(false);
+ }).not.toThrow();
+ });
+
+ it('does not accept a string', function () {
+ expect(function () {
+ dtp.extraFormats('YYYY-MM-DD');
+ }).toThrow();
+ });
+
+ it('does not accept undefined', function () {
+ expect(function () {
+ dtp.extraFormats(undefined);
+ }).toThrow();
+ });
+
+ it('does not accept true', function () {
+ expect(function () {
+ dtp.extraFormats(true);
+ }).toThrow();
+ });
+
+ it('accepts an Array', function () {
+ expect(function () {
+ dtp.extraFormats(['YYYY-MM-DD']);
+ }).not.toThrow();
+ });
+ });
+
+ describe('functionality', function () {
+ it('returns no extraFormats before extraFormats is set', function () {
+ expect(dtp.extraFormats()).toBe(false);
+ });
+
+ it('sets the extraFormats correctly', function () {
+ dtp.extraFormats(['YYYY-MM-DD']);
+ expect(dtp.extraFormats()[0]).toBe('YYYY-MM-DD');
+ });
+ });
+ });
+
+ describe('toolbarPlacement() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.toolbarPlacement).toBeDefined();
+ });
+ });
+ describe('check type and parameter validity', function () {
+ it('does not accept a false value', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat(false);
+ }).toThrow();
+ });
+ it('does not accept a false value', function () {
+ expect(function () {
+ dtp.dayViewHeaderFormat(false);
+ }).toThrow();
+ });
+ it('accepts a string', function () {
+ var toolbarPlacementOptions = ['default', 'top', 'bottom'];
+
+ toolbarPlacementOptions.forEach(function (value) {
+ expect(function () {
+ dtp.toolbarPlacement(value);
+ }).not.toThrow();
+ });
+
+ expect(function () {
+ dtp.toolbarPlacement('test');
+ }).toThrow();
+ expect(function () {
+ dtp.toolbarPlacement({});
+ }).toThrow();
+ });
+ });
+ });
+
+ describe('widgetParent() function', function () {
+ describe('typechecking', function () {
+ it('accepts a null', function () {
+ expect(function () {
+ dtp.widgetParent(null);
+ }).not.toThrow();
+ });
+
+ it('accepts a string', function () {
+ expect(function () {
+ dtp.widgetParent('testDiv');
+ }).not.toThrow();
+ });
+
+ it('accepts a jquery object', function () {
+ expect(function () {
+ dtp.widgetParent($('#testDiv'));
+ }).not.toThrow();
+ });
+
+ it('does not accept undefined', function () {
+ expect(function () {
+ dtp.widgetParent(undefined);
+ }).toThrow();
+ });
+
+ it('does not accept a number', function () {
+ expect(function () {
+ dtp.widgetParent(0);
+ }).toThrow();
+ });
+
+ it('does not accept a generic Object', function () {
+ expect(function () {
+ dtp.widgetParent({});
+ }).toThrow();
+ });
+
+ it('does not accept a boolean', function () {
+ expect(function () {
+ dtp.widgetParent(false);
+ }).toThrow();
+ });
+ });
+ });
+
+ describe('keepOpen() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.keepOpen).toBeDefined();
+ });
+ });
+ });
+
+ describe('inline() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.inline).toBeDefined();
+ });
+ });
+ });
+
+ describe('clear() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.clear).toBeDefined();
+ });
+ });
+ });
+
+ describe('keyBinds() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.keyBinds).toBeDefined();
+ });
+ });
+ });
+
+ describe('parseInputDate() function', function () {
+ describe('existence', function () {
+ it('is defined', function () {
+ expect(dtp.parseInputDate).toBeDefined();
+ });
+ });
+ });
+});
diff --git a/test/screen-capture/base.html b/test/screen-capture/base.html
new file mode 100644
index 0000000..234887c
--- /dev/null
+++ b/test/screen-capture/base.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+<head>
+<link rel="stylesheet" type="text/css" href="../../../node_modules/bootstrap/dist/css/bootstrap.css">
+<link rel="stylesheet" type="text/css" href="../../../build/css/bootstrap-datetimepicker.css">
+<script src="../../../node_modules/jquery/dist/jquery.min.js"></script>
+<script src="../../../node_modules/bootstrap/dist/js/bootstrap.js"></script>
+<script src="../../../node_modules/moment/moment.js"></script>
+<script src="../../../src/js/bootstrap-datetimepicker.js"></script>
+<style>
+.parent{
+ position: relative;
+ padding: 40px;
+}
+.helper{
+ z-index: -1;
+ position: absolute;
+ background-color: #bfb;
+ top: 0px;
+ bottom: 0px;
+ right: 0px;
+ left: 0px;
+ opacity: 0.3;
+}
+.inner{
+ position: absolute;
+ background-color: #bbf;
+ top: 40px;
+ bottom: 40px;
+ right: 40px;
+ left: 40px;
+}
+</style>
+</head>
+<body>
+<div class="container">
+ <div class="row">
+ <div class="col-md-6 col-md-offset-3">
+ <h1>{{v}} - {{h}}</h1>
+ <br><br><br><br><br><br><br><br><br><br>
+ <div class="parent">
+ <div class="helper">
+ <div class="inner"></div>
+ </div>
+ <br><br><br><br><br>
+
+ {{{t}}}
+
+ <br><br><br><br><br>
+ </div>
+ <br><br><br><br><br><br><br><br><br><br><br><br>
+ </div>
+ </div>
+</div>
+<script type="text/javascript">
+ $('[data-datetimepicker]').datetimepicker({
+ widgetPositioning:{
+ vertical: '{{v}}',
+ horizontal: '{{h}}'
+ }
+ });
+ $('input[data-click-target]').focus();
+ $('span[data-click-target]').click();
+</script>
+</body>
diff --git a/test/screen-capture/compile.js b/test/screen-capture/compile.js
new file mode 100644
index 0000000..dc9c012
--- /dev/null
+++ b/test/screen-capture/compile.js
@@ -0,0 +1,17 @@
+var fs = require("fs");
+
+var base = fs.readFileSync('base.html').toString();
+
+['top','bottom'].forEach(function(v){
+ ['left','right'].forEach(function(h){
+ ['1','2','3','4','5'].forEach(function(t){
+ var text = fs.readFileSync('t' +t+'.html').toString();
+ var outFile = 'out/' + t +v.charAt(0) + h.charAt(0) + '.html';
+ var out = base
+ .replace(/\{\{\{t\}\}\}/g,text)
+ .replace(/\{\{v\}\}/g,v)
+ .replace(/\{\{h\}\}/g,h);
+ fs.writeFileSync(outFile, out);
+ });
+ });
+});
diff --git a/test/screen-capture/index.js b/test/screen-capture/index.js
new file mode 100644
index 0000000..d114c69
--- /dev/null
+++ b/test/screen-capture/index.js
@@ -0,0 +1,24 @@
+var fs = require('fs');
+['top','bottom'].forEach(function(v){
+ ['left','right'].forEach(function(h){
+ ['1','2','3','4','5'].forEach(function(t){
+ var inFile = 'out/'+ t +v.charAt(0) + h.charAt(0) + '.html';
+ var outFile = 'pic/'+ t + v.charAt(0) + h.charAt(0) + '.png';
+ var path = 'file://' + fs.absolute(inFile)
+ var page = require('webpage').create();
+ page.viewportSize = {
+ width: 1000,
+ height: 800
+ };;
+ page.open(path, function(status) {
+ window.setTimeout(function () {
+ console.log(status);
+ page.render(outFile);
+ setTimeout(function(){
+ phantom.exit();
+ }, 0);
+ },2000);
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/screen-capture/out/.gitignore b/test/screen-capture/out/.gitignore
new file mode 100644
index 0000000..2d19fc7
--- /dev/null
+++ b/test/screen-capture/out/.gitignore
@@ -0,0 +1 @@
+*.html
diff --git a/test/screen-capture/pic/.gitignore b/test/screen-capture/pic/.gitignore
new file mode 100644
index 0000000..e33609d
--- /dev/null
+++ b/test/screen-capture/pic/.gitignore
@@ -0,0 +1 @@
+*.png
diff --git a/test/screen-capture/t1.html b/test/screen-capture/t1.html
new file mode 100644
index 0000000..7686408
--- /dev/null
+++ b/test/screen-capture/t1.html
@@ -0,0 +1,11 @@
+<div class="input-group">
+ <span class="input-group-addon">
+ start time
+ </span>
+ <input type="text" name="filter.startTime[]" class="form-control" placeholder="start time" id="start-time-input" value="" data-datetimepicker data-click-target>
+ <span class="input-group-addon">
+ end time
+ </span>
+ <input type="text" name="filter.endTime[]" class="form-control" placeholder="end time" id="end-time-input" value=""
+ data-datetimepicker>
+ </div>
\ No newline at end of file
diff --git a/test/screen-capture/t2.html b/test/screen-capture/t2.html
new file mode 100644
index 0000000..6340505
--- /dev/null
+++ b/test/screen-capture/t2.html
@@ -0,0 +1,7 @@
+<div class="input-group">
+ <span class="input-group-addon">
+ end time
+ </span>
+ <input type="text" name="filter.endTime[]" class="form-control" placeholder="end time" id="end-time-input" value=""
+ data-datetimepicker data-click-target>
+ </div>
diff --git a/test/screen-capture/t3.html b/test/screen-capture/t3.html
new file mode 100644
index 0000000..55d3488
--- /dev/null
+++ b/test/screen-capture/t3.html
@@ -0,0 +1,6 @@
+<div class='input-group date' data-datetimepicker>
+ <input type='text' class="form-control" />
+ <span class="input-group-addon">
+ <span class="glyphicon glyphicon-calendar" data-click-target></span>
+ </span>
+ </div>
\ No newline at end of file
diff --git a/test/screen-capture/t4.html b/test/screen-capture/t4.html
new file mode 100644
index 0000000..31c10b6
--- /dev/null
+++ b/test/screen-capture/t4.html
@@ -0,0 +1,6 @@
+<div class='input-group date' data-datetimepicker>
+ <span class="input-group-addon" data-click-target>
+ <span class="glyphicon glyphicon-calendar"></span>
+ </span>
+ <input type='text' class="form-control" />
+ </div>
\ No newline at end of file
diff --git a/test/screen-capture/t5.html b/test/screen-capture/t5.html
new file mode 100644
index 0000000..20c6847
--- /dev/null
+++ b/test/screen-capture/t5.html
@@ -0,0 +1 @@
+<input type='text' class="form-control" data-datetimepicker data-click-target/>
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/eonasdan-bootstrap-datetimepicker.git
More information about the Pkg-javascript-commits
mailing list