├── .github ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml ├── ISSUE_TEMPLATE.md └── CONTRIBUTING.md ├── src ├── TimePickerAsset.php ├── assets │ ├── css │ │ ├── bootstrap-timepicker.min.css │ │ └── bootstrap-timepicker.css │ └── js │ │ ├── bootstrap-timepicker.min.js │ │ └── bootstrap-timepicker.js └── TimePicker.php ├── composer.json ├── CHANGE.md ├── LICENSE.md └── README.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Scope 2 | This pull request includes a 3 | 4 | - [ ] Bug fix 5 | - [ ] New feature 6 | - [ ] Translation 7 | 8 | ## Changes 9 | The following changes were made (this change is also documented in the [change log](https://github.com/kartik-v/yii2-widget-timepicker/blob/master/CHANGE.md)): 10 | 11 | - 12 | - 13 | - 14 | 15 | ## Related Issues 16 | If this is related to an existing ticket, include a link to it as well. -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - bug 8 | - enhancement 9 | - pinned 10 | - security 11 | # Label to use when marking an issue as stale 12 | staleLabel: wontfix 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. Thank you 17 | for your contributions. 18 | # Comment to post when closing a stale issue. Set to `false` to disable 19 | closeComment: false -------------------------------------------------------------------------------- /src/TimePickerAsset.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 1.0 19 | */ 20 | class TimePickerAsset extends AssetBundle 21 | { 22 | /** 23 | * @inheritdoc 24 | */ 25 | public function init() 26 | { 27 | $this->setSourcePath(__DIR__ . '/assets'); 28 | $this->setupAssets('css', ['css/bootstrap-timepicker']); 29 | $this->setupAssets('js', ['js/bootstrap-timepicker']); 30 | parent::init(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kartik-v/yii2-widget-timepicker", 3 | "description": "Enhanced Yii2 wrapper for the bootstrap timepicker plugin (sub repo split from yii2-widgets)", 4 | "keywords": [ 5 | "yii2", 6 | "extension", 7 | "widget", 8 | "form", 9 | "time", 10 | "picker", 11 | "bootstrap", 12 | "jquery", 13 | "plugin" 14 | ], 15 | "homepage": "https://github.com/kartik-v/yii2-widget-timepicker", 16 | "type": "yii2-extension", 17 | "license": "BSD-3-Clause", 18 | "authors": [ 19 | { 20 | "name": "Kartik Visweswaran", 21 | "email": "kartikv2@gmail.com", 22 | "homepage": "http://www.krajee.com/" 23 | } 24 | ], 25 | "require": { 26 | "kartik-v/yii2-krajee-base": ">=2.0.0" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "kartik\\time\\": "src" 31 | } 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "1.0.x-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CHANGE.md: -------------------------------------------------------------------------------- 1 | Change Log: `yii2-widget-timepicker` 2 | ==================================== 3 | 4 | ## Version 1.0.5 5 | 6 | **Date:** 28-Oct-2021 7 | 8 | - Enhancements to support Bootstrap v5.x. 9 | 10 | ## Version 1.0.4 11 | 12 | **Date:** 09-Oct-2018 13 | 14 | - Bump composer version dependencies. 15 | - Add Bootstrap 4.x support. 16 | - Reorganize source code in `src` directory. 17 | 18 | ## Version 1.0.3 19 | 20 | **Date:** 08-Jan-2017 21 | 22 | - (enh #12): Correct timepicker change initialization. 23 | 24 | ## Version 1.0.2 25 | 26 | **Date:** 04-Jan-2017 27 | 28 | - (enh #12): Prevent timepicker plugin triggering change on init. 29 | - Add github contribution and issue/PR logging templates. 30 | - Add branch alias for dev-master latest release. 31 | 32 | ## Version 1.0.1 33 | 34 | **Date:** 12-Jan-2016 35 | 36 | - enh #7: Enhancements for PJAX based reinitialization. Complements enhancements in kartik-v/yii2-krajee-base#52 and kartik-v/yii2-krajee-base#53. 37 | 38 | ## Version 1.0.0 39 | 40 | **Date:** 08-Nov-2014 41 | 42 | - Initial release 43 | - Sub repo split from [yii2-widgets](https://github.com/kartik-v/yii2-widgets) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | - [ ] I have searched for similar issues in both open and closed tickets and cannot find a duplicate. 4 | - [ ] The issue still exists against the latest `master` branch of yii2-widget-timepicker. 5 | - [ ] This is not an usage question. I confirm having gone through and read the [documentation and demos](http://demos.krajee.com/widget-details/timepicker). 6 | - [ ] This is not a general programming / coding question. (Those should be directed to the [webtips Q & A forum](http://webtips.krajee.com/questions)). 7 | - [ ] I have attempted to find the simplest possible steps to reproduce the issue. 8 | - [ ] I have included a failing test as a pull request (Optional). 9 | 10 | ## Steps to reproduce the issue 11 | 12 | 1. 13 | 2. 14 | 3. 15 | 16 | ## Expected behavior and actual behavior 17 | 18 | When I follow those steps, I see... 19 | 20 | I was expecting... 21 | 22 | ## Environment 23 | 24 | #### Browsers 25 | 26 | - [ ] Google Chrome 27 | - [ ] Mozilla Firefox 28 | - [ ] Internet Explorer 29 | - [ ] Safari 30 | 31 | #### Operating System 32 | 33 | - [ ] Windows 34 | - [ ] Mac OS X 35 | - [ ] Linux 36 | - [ ] Mobile 37 | 38 | #### Libraries 39 | 40 | - jQuery version: 41 | - yii2-widget-timepicker version: 42 | 43 | ## Isolating the problem 44 | 45 | - [ ] This bug happens [on the demos page](https://demos.krajee.com/widget-details/timepicker) 46 | - [ ] The bug happens consistently across all tested browsers 47 | - [ ] This bug happens when using yii2-widget-timepicker without other plugins. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 - 2021, Kartik Visweswaran 2 | Krajee.com 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | * Neither the names of Kartik Visweswaran or Krajee nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/assets/css/bootstrap-timepicker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Timepicker Component for Twitter Bootstrap 3 | * 4 | * Improvements by: Kartik Visweswaran, Krajee.com, 2014 - 2021 5 | * 6 | * Copyright 2013 Joris de Wit 7 | * 8 | * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */.bootstrap-timepicker{position:relative}.bootstrap-timepicker-widget{min-width:19rem}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu{left:auto;right:0}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before{left:auto;right:.75rem}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after{left:auto;right:.8125rem}.bootstrap-timepicker .disabled-addon{cursor:not-allowed!important}.bootstrap-timepicker .inline-addon{position:absolute;display:inline-block;top:.675rem;right:.625rem;line-height:1.5;z-index:3}.bootstrap-timepicker .inline-addon-lg{font-size:1.875rem}.bootstrap-timepicker .inline-addon-sm{font-size:1.2rem}.bootstrap-timepicker4 .inline-addon{top:.5rem}.bootstrap-timepicker3 .inline-addon-lg{top:1.05rem}.bootstrap-timepicker4 .inline-addon-lg{font-size:1.25rem}.bootstrap-timepicker4 .inline-addon-sm{top:.375rem;font-size:.85rem}.bootstrap-timepicker-widget.dropdown-menu{padding:.125rem .1875rem .125rem .125rem}.bootstrap-timepicker-widget.dropdown-menu.open{display:inline-block}.bootstrap-timepicker-widget.dropdown-menu:before{border-bottom:.4375rem solid rgba(0,0,0,.2);border-left:.4375rem solid transparent;border-right:.4375rem solid transparent;content:"";display:inline-block;left:.5625rem;position:absolute;top:-.4375rem}.bootstrap-timepicker-widget.dropdown-menu:after{border-bottom:.375rem solid #FFF;border-left:.375rem solid transparent;border-right:.375rem solid transparent;content:"";display:inline-block;left:.625rem;position:absolute;top:-.375rem}.bootstrap-timepicker-widget a.btn,.bootstrap-timepicker-widget input{border-radius:.25rem!important}.bootstrap-timepicker input{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.bootstrap-timepicker-widget table{width:100%;margin:0}.bootstrap-timepicker-widget table td{text-align:center;height:1.875rem;margin:0;padding:.125rem}.bootstrap-timepicker .separator{font-weight:700;font-family:Monaco,Consolas,monospace}.bootstrap-timepicker-widget table td:not(.separator){min-width:1.875rem}.bootstrap-timepicker-widget table td span{width:100%}.bootstrap-timepicker-widget table td a{border:.0625rem solid transparent;width:100%;display:inline-block;margin:0;padding:.125rem 0;outline:0;color:#333}.bootstrap-timepicker-widget table td a:hover{text-decoration:none;background-color:#eee;-webkit-border-radius:.25rem;-moz-border-radius:.25rem;border-radius:.25rem;border-color:#ddd}.bootstrap-timepicker-widget table td a i{margin-top:.125rem}.bootstrap-timepicker-widget table td input{margin:0;text-align:center}.bootstrap-timepicker-widget .modal-content{padding:.25rem}.bootstrap-timepicker .picker{cursor:pointer}@media (min-width:767px){.bootstrap-timepicker-widget.modal{width:200px;margin-left:-100px}}@media (max-width:767px){.bootstrap-timepicker,.bootstrap-timepicker .dropdown-menu{width:100%}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Krajee Logo 4 | 5 |
6 | yii2-widget-timepicker 7 |
8 | Donate 10 |       11 | kartikv 12 |

13 | 14 |
15 | 16 | [![Stable Version](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/v/stable)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 17 | [![Untable Version](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/v/unstable)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 18 | [![License](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/license)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 19 | [![Total Downloads](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/downloads)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 20 | [![Monthly Downloads](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/d/monthly)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 21 | [![Daily Downloads](https://poser.pugx.org/kartik-v/yii2-widget-timepicker/d/daily)](https://packagist.org/packages/kartik-v/yii2-widget-timepicker) 22 | 23 |
24 | 25 | The TimePicker widget allows you to easily select a time for a text input using your mouse or keyboards arrow keys. The widget is a wrapper enhancement of the TimePicker plugin by rendom forked from jdewit's TimePicker. This widget as used here has been specially enhanced for Yii framework 2.0 and Bootstrap 3. With release v1.0.4, the extension has been enhanced to support Bootstrap 4.x version. 26 | 27 | > NOTE: This extension is a sub repo split of [yii2-widgets](https://github.com/kartik-v/yii2-widgets). The split has been done since 08-Nov-2014 to allow developers to install this specific widget in isolation if needed. One can also use the extension the previous way with the whole suite of [yii2-widgets](http://demos.krajee.com/widgets). 28 | 29 | ## Installation 30 | 31 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). Check the [composer.json](https://github.com/kartik-v/yii2-widget-timepicker/blob/master/composer.json) for this extension's requirements and dependencies. Read this [web tip /wiki](http://webtips.krajee.com/setting-composer-minimum-stability-application/) on setting the `minimum-stability` settings for your application's composer.json. 32 | 33 | To install, either run 34 | 35 | ``` 36 | $ php composer.phar require kartik-v/yii2-widget-timepicker "*" 37 | ``` 38 | 39 | or add 40 | 41 | ``` 42 | "kartik-v/yii2-widget-timepicker": "*" 43 | ``` 44 | 45 | to the ```require``` section of your `composer.json` file. 46 | 47 | ## Release Changes 48 | 49 | > NOTE: Refer the [CHANGE LOG](https://github.com/kartik-v/yii2-widget-timepicker/blob/master/CHANGE.md) for details on changes to various releases. 50 | 51 | ## Demo 52 | 53 | You can refer detailed [documentation and demos](http://demos.krajee.com/widget-details/timepicker) on usage of the extension. 54 | 55 | ## Usage 56 | 57 | ```php 58 | use kartik\time\TimePicker; 59 | 60 | // usage without model 61 | echo ''; 62 | echo TimePicker::widget([ 63 | 'name' => 'start_time', 64 | 'value' => '11:24 AM', 65 | 'pluginOptions' => [ 66 | 'showSeconds' => true 67 | ] 68 | ]); 69 | ``` 70 | 71 | ## License 72 | 73 | **yii2-widget-timepicker** is released under the BSD-3-Clause License. See the bundled `LICENSE.md` for details. -------------------------------------------------------------------------------- /src/assets/css/bootstrap-timepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Timepicker Component for Twitter Bootstrap 3 | * 4 | * Improvements by: Kartik Visweswaran, Krajee.com, 2014 - 2021 5 | * 6 | * Copyright 2013 Joris de Wit 7 | * 8 | * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | .bootstrap-timepicker { 14 | position: relative; 15 | } 16 | 17 | .bootstrap-timepicker-widget { 18 | min-width: 19rem; 19 | } 20 | 21 | .bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu { 22 | left: auto; 23 | right: 0; 24 | } 25 | 26 | .bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before { 27 | left: auto; 28 | right: 0.75rem; 29 | } 30 | 31 | .bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after { 32 | left: auto; 33 | right: 0.8125rem; 34 | } 35 | 36 | .bootstrap-timepicker .disabled-addon { 37 | cursor: not-allowed !important; 38 | } 39 | 40 | .bootstrap-timepicker .inline-addon { 41 | position: absolute; 42 | display: inline-block; 43 | top: 0.675rem; 44 | right: 0.625rem; 45 | line-height: 1.5; 46 | z-index: 3; 47 | } 48 | 49 | .bootstrap-timepicker .inline-addon-lg { 50 | font-size: 1.875rem; 51 | } 52 | 53 | .bootstrap-timepicker .inline-addon-sm { 54 | font-size: 1.2rem; 55 | } 56 | 57 | .bootstrap-timepicker4 .inline-addon { 58 | top: 0.5rem; 59 | } 60 | 61 | .bootstrap-timepicker3 .inline-addon-lg { 62 | top: 1.05rem; 63 | } 64 | 65 | .bootstrap-timepicker4 .inline-addon-lg { 66 | font-size: 1.25rem; 67 | } 68 | 69 | .bootstrap-timepicker4 .inline-addon-sm { 70 | top: 0.375rem; 71 | font-size: 0.85rem; 72 | } 73 | 74 | .bootstrap-timepicker-widget.dropdown-menu { 75 | padding: 0.125rem 0.1875rem 0.125rem 0.125rem; 76 | } 77 | 78 | .bootstrap-timepicker-widget.dropdown-menu.open { 79 | display: inline-block; 80 | } 81 | 82 | .bootstrap-timepicker-widget.dropdown-menu:before { 83 | border-bottom: 0.4375rem solid rgba(0, 0, 0, 0.2); 84 | border-left: 0.4375rem solid transparent; 85 | border-right: 0.4375rem solid transparent; 86 | content: ""; 87 | display: inline-block; 88 | left: 0.5625rem; 89 | position: absolute; 90 | top: -0.4375rem; 91 | } 92 | 93 | .bootstrap-timepicker-widget.dropdown-menu:after { 94 | border-bottom: 0.375rem solid #FFFFFF; 95 | border-left: 0.375rem solid transparent; 96 | border-right: 0.375rem solid transparent; 97 | content: ""; 98 | display: inline-block; 99 | left: 0.625rem; 100 | position: absolute; 101 | top: -0.375rem; 102 | } 103 | 104 | .bootstrap-timepicker-widget a.btn, 105 | .bootstrap-timepicker-widget input { 106 | border-radius: 0.25rem !important; 107 | } 108 | 109 | .bootstrap-timepicker input { 110 | border-top-left-radius: 0.25rem !important; 111 | border-bottom-left-radius: 0.25rem !important; 112 | } 113 | 114 | .bootstrap-timepicker-widget table { 115 | width: 100%; 116 | margin: 0; 117 | } 118 | 119 | .bootstrap-timepicker-widget table td { 120 | text-align: center; 121 | height: 1.875rem; 122 | margin: 0; 123 | padding: 0.125rem; 124 | } 125 | 126 | .bootstrap-timepicker .separator { 127 | font-weight: bold; 128 | font-family: Monaco, Consolas, monospace; 129 | } 130 | 131 | .bootstrap-timepicker-widget table td:not(.separator) { 132 | min-width: 1.875rem; 133 | } 134 | 135 | .bootstrap-timepicker-widget table td span { 136 | width: 100%; 137 | } 138 | 139 | .bootstrap-timepicker-widget table td a { 140 | border: 0.0625rem transparent solid; 141 | width: 100%; 142 | display: inline-block; 143 | margin: 0; 144 | padding: 0.125rem 0; 145 | outline: 0; 146 | color: #333; 147 | } 148 | 149 | .bootstrap-timepicker-widget table td a:hover { 150 | text-decoration: none; 151 | background-color: #eee; 152 | -webkit-border-radius: 0.25rem; 153 | -moz-border-radius: 0.25rem; 154 | border-radius: 0.25rem; 155 | border-color: #ddd; 156 | } 157 | 158 | .bootstrap-timepicker-widget table td a i { 159 | margin-top: 0.125rem; 160 | } 161 | 162 | .bootstrap-timepicker-widget table td input { 163 | margin: 0; 164 | text-align: center; 165 | } 166 | 167 | .bootstrap-timepicker-widget .modal-content { 168 | padding: 0.25rem; 169 | } 170 | 171 | .bootstrap-timepicker .picker { 172 | cursor: pointer; 173 | } 174 | 175 | @media (min-width: 767px) { 176 | .bootstrap-timepicker-widget.modal { 177 | width: 200px; 178 | margin-left: -100px; 179 | } 180 | } 181 | 182 | @media (max-width: 767px) { 183 | .bootstrap-timepicker { 184 | width: 100%; 185 | } 186 | 187 | .bootstrap-timepicker .dropdown-menu { 188 | width: 100%; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/TimePicker.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 1.0 25 | * @see https://github.com/rendom/bootstrap-3-timepicker 26 | * @see https://github.com/jdewit/bootstrap-timepicker 27 | */ 28 | class TimePicker extends InputWidget 29 | { 30 | /** 31 | * @var string the size of the input - 'lg', 'md', 'sm', 'xs' 32 | */ 33 | public $size; 34 | 35 | /** 36 | * @var string|boolean the addon content 37 | */ 38 | public $addon; 39 | 40 | /** 41 | * @var array HTML attributes for the addon container. The following special options are recognized: 42 | * - `asButton`: _boolean_, if the addon is to be displayed as a button. 43 | * - `buttonOptions`: _array_, HTML attributes if the addon is to be displayed like a button. If [[asButton]] is 44 | * `true`, this will default to : 45 | * - `['class' => 'btn btn-default']` for [[bsVersion]] = '3.x' or . 46 | * - `['class' => 'btn btn-secondary']` for [[bsVersion]] = '4.x' and '5.x' 47 | */ 48 | public $addonOptions = []; 49 | 50 | /** 51 | * @var array HTML attributes for the input group container 52 | */ 53 | public $containerOptions = []; 54 | 55 | /** 56 | * @inheritdoc 57 | */ 58 | public $pluginName = 'timepicker'; 59 | 60 | /** 61 | * @inheritdoc 62 | * @throws InvalidConfigException 63 | */ 64 | public function run() 65 | { 66 | $this->initIcon('up'); 67 | $this->initIcon('down'); 68 | $this->registerAssets(); 69 | echo Html::tag('div', $this->renderInput(), $this->containerOptions); 70 | } 71 | 72 | /** 73 | * Initializes icon for time units up and down buttons 74 | * @param string $type whether 'up' or 'down' 75 | * @throws InvalidConfigException|\Exception 76 | */ 77 | protected function initIcon($type) 78 | { 79 | $prop = $type . 'ArrowStyle'; 80 | if (!isset($this->pluginOptions[$prop])) { 81 | $prefix = !$this->isBs(3) ? 'fas fa-' : 'glyphicon glyphicon-'; 82 | $this->pluginOptions[$prop] = $prefix . 'chevron-' . $type; 83 | } 84 | } 85 | 86 | /** 87 | * Renders the input 88 | * 89 | * @return string 90 | * @throws InvalidConfigException|\Exception 91 | */ 92 | protected function renderInput() 93 | { 94 | $notBs3 = !$this->isBs(3); 95 | $isBs5 = $this->isBs(5); 96 | if (!isset($this->addon)) { 97 | $this->addon = $notBs3 ? '' : ''; 98 | } 99 | Html::addCssClass($this->options, 'form-control'); 100 | if (!empty($this->options['disabled'])) { 101 | Html::addCssClass($this->addonOptions, 'disabled-addon'); 102 | } 103 | if (ArrayHelper::getValue($this->pluginOptions, 'template', true) === false) { 104 | $css = $notBs3 ? 'bootstrap-timepicker4' : 'bootstrap-timepicker3'; 105 | Html::addCssClass($this->containerOptions, ['bootstrap-timepicker', $css]); 106 | if (isset($this->size)) { 107 | Html::addCssClass($this->options, ($notBs3 ? 'form-control-' : 'input-') . $this->size); 108 | Html::addCssClass($this->addonOptions, 'inline-addon inline-addon-' . $this->size); 109 | } else { 110 | Html::addCssClass($this->addonOptions, 'inline-addon'); 111 | } 112 | return $this->getInput('textInput') . Html::tag('span', $this->addon, $this->addonOptions); 113 | } 114 | Html::addCssClass($this->containerOptions, 'bootstrap-timepicker input-group'); 115 | $asButton = ArrayHelper::remove($this->addonOptions, 'asButton', false); 116 | $buttonOptions = ArrayHelper::remove($this->addonOptions, 'buttonOptions', []); 117 | 118 | if ($asButton) { 119 | $css = $notBs3 ? 'input-group-append' : 'input-group-btn'; 120 | $tag = $notBs3 ? 'div' : 'span'; 121 | Html::addCssClass($this->addonOptions, [$css, 'picker']); 122 | $buttonOptions['type'] = 'button'; 123 | if (empty($buttonOptions['class'])) { 124 | Html::addCssClass($buttonOptions, 'btn btn-default'); 125 | } 126 | $button = Html::button($this->addon, $buttonOptions); 127 | $addon = $isBs5 ? $button : Html::tag($tag, $button, $this->addonOptions); 128 | } else { 129 | $css = $notBs3 ? 'input-group-text' : 'input-group-addon'; 130 | Html::addCssClass($this->addonOptions, [$css, 'picker']); 131 | $addon = Html::tag('span', $this->addon, $this->addonOptions); 132 | if ($notBs3 && !$isBs5) { 133 | $addon = Html::tag('div', $addon, ['class' => 'input-group-append']); 134 | } 135 | } 136 | if (isset($this->size)) { 137 | Html::addCssClass($this->containerOptions, 'input-group-' . $this->size); 138 | } 139 | return $this->getInput('textInput') . $addon; 140 | } 141 | 142 | /** 143 | * Registers the client assets for [[Timepicker]] widget 144 | */ 145 | public function registerAssets() 146 | { 147 | $view = $this->getView(); 148 | TimePickerAsset::register($view); 149 | $this->registerPlugin($this->pluginName); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to yii2-widget-timepicker 2 | ====================================== 3 | Looking to contribute something to yii2-widget-timepicker? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | Using the issue tracker 14 | ----------------------- 15 | When [reporting bugs][reporting-bugs] or 16 | [requesting features][requesting-features], the 17 | [issue tracker on GitHub][issue-tracker] is the recommended channel to use. 18 | 19 | The issue tracker **is not** a place for support requests. Refer the 20 | [extension documentation and demos](http://demos.krajee.com/widget-details/timepicker) and/or refer to the 21 | [webtips Q & A forum](http://webtips.krajee.com/questions) which are the better places to get help. 22 | 23 | Reporting bugs with yii2-widget-timepicker 24 | ------------------------------------------ 25 | 26 | We really appreciate clear bug reports that _consistently_ show an issue 27 | _within yii2-widget-timepicker_. 28 | 29 | The ideal bug report follows these guidelines: 30 | 31 | 1. **Use the [GitHub issue search][issue-search]** — Check if the issue 32 | has already been reported. 33 | 2. **Check if the issue has been fixed** — Try to reproduce the problem 34 | using the code in the `master` branch. 35 | 3. **Isolate the problem** — Try to share a demo or a test case that 36 | consistently reproduces the problem. 37 | 38 | Please try to be as detailed as possible in your bug report, especially if an 39 | isolated test case cannot be made. Some useful questions to include the answer 40 | to are: 41 | 42 | - What steps can be used to reproduce the issue? 43 | - What is the bug and what is the expected outcome? 44 | - What browser(s) and Operating System have you tested with? 45 | - Does the bug happen consistently across all tested browsers? 46 | - What version of jQuery are you using? And what version of yii2-widget-timepicker? 47 | - Are you using yii2-widget-timepicker with other plugins? 48 | 49 | All of these questions will help others fix and identify any potential bugs. 50 | 51 | Requesting features in yii2-widget-timepicker 52 | ------------------------------------------ 53 | Before starting work on a major feature for yii2-widget-timepicker, **read the 54 | [documentation](http://demos.krajee.com/widget-details/timepicker) first** or you may risk spending a considerable amount of 55 | time on something which the project developers are not interested in bringing into the project. 56 | 57 | ### Submitting a pull request 58 | 59 | We use GitHub's pull request system for submitting patches. Here are some 60 | guidelines to follow when creating the pull request for your fix. 61 | 62 | 1. Make sure to create a ticket for your pull request. This will serve as the 63 | bug ticket, and any discussion about the bug will take place there. Your pull 64 | request will be focused on the specific changes that fix the bug. 65 | 2. Make sure to reference the ticket you are fixing within your pull request. 66 | This will allow us to close off the ticket once we merge the pull request, or 67 | follow up on the ticket if there are any related blocking issues. 68 | 3. Explain why the specific change was made. Not everyone who is reviewing your 69 | pull request will be familiar with the problem it is fixing. 70 | 4. Run your tests first. If your tests aren't passing, the pull request won't 71 | be able to be merged. If you're breaking existing tests, make sure that you 72 | aren't causing any breaking changes. 73 | 5. Only include source changes. While it's not required, only including changes 74 | from the `src` directory will prevent merge conflicts from occuring. Making 75 | this happen can be as a simple as not committing changes from the `dist` 76 | directory. 77 | 78 | By following these steps, you will make it easier for your pull request to be 79 | reviewed and eventually merged. 80 | 81 | Triaging issues and pull requests 82 | --------------------------------- 83 | Anyone can help the project maintainers triage issues and review pull requests. 84 | 85 | ### Handling new issues 86 | 87 | yii2-widget-timepicker regularly receives new issues which need to be tested and organized. 88 | 89 | When a new issue that comes in that is similar to another existing issue, it 90 | should be checked to make sure it is not a duplicate. Duplicates issues should 91 | be marked by replying to the issue with "Duplicate of #[issue number]" where 92 | `[issue number]` is the url or issue number for the existing issue. This will 93 | allow the project maintainers to quickly close off additional issues and keep 94 | the discussion focused within a single issue. 95 | 96 | If you can test issues that are reported to yii2-widget-timepicker that contain test cases and 97 | confirm under what conditions bugs happen, that will allow others to identify 98 | what causes a bug quicker. 99 | 100 | ### Reviewing pull requests 101 | 102 | It is very common for pull requests to be opened for issues that contain a clear 103 | solution to the problem. These pull requests should be rigorously reviewed by 104 | the community before being accepted. If you are not sure about a piece of 105 | submitted code, or know of a better way to do something, do not hesitate to make 106 | a comment on the pull request. 107 | 108 | ### Reviving old tickets 109 | 110 | If you come across tickets which have not been updated for a while, you are 111 | encouraged to revive them. While this can be as simple as saying `:+1:`, it is 112 | best if you can include more information on the issue. Common bugs and feature 113 | requests are more likely to be fixed, whether it is by the community or the 114 | developers, so keeping tickets up to date is encouraged. 115 | 116 | Licensing 117 | --------- 118 | 119 | It should also be made clear that **all code contributed to yii2-widget-timepicker** must be 120 | licensable under the [BSD-3 license][licensing]. Code that cannot be released 121 | under this license **cannot be accepted** into the project. 122 | 123 | [issue-search]: https://github.com/kartik-v/yii2-widget-timepicker/search?q=&type=Issues 124 | [issue-tracker]: https://github.com/kartik-v/yii2-widget-timepicker/issues 125 | [licensing]: https://github.com/kartik-v/yii2-widget-timepicker/blob/master/LICENSE.md 126 | [reporting-bugs]: #reporting-bugs-with-yii2-widget-timepicker 127 | [requesting-features]: #requesting-features-in-yii2-widget-timepicker -------------------------------------------------------------------------------- /src/assets/js/bootstrap-timepicker.min.js: -------------------------------------------------------------------------------- 1 | /*! bootstrap-timepicker v0.2.3 2 | * Improvements by: Kartik Visweswaran, Krajee.com, 2014 - 2021 3 | * http://jdewit.github.com/bootstrap-timepicker 4 | * Copyright (c) 2013 Joris de Wit 5 | * MIT License 6 | */!function(t,i,e,s){"use strict";var h=function(i,e){this.widget="",this.$element=t(i),this.defaultTime=e.defaultTime,this.disableFocus=e.disableFocus,this.isOpen=e.isOpen,this.minuteStep=e.minuteStep,this.modalBackdrop=e.modalBackdrop,this.secondStep=e.secondStep,this.showInputs=e.showInputs,this.showMeridian=e.showMeridian,this.showSeconds=e.showSeconds,this.template=e.template,this.appendWidgetTo=e.appendWidgetTo,this.upArrowStyle=e.upArrowStyle,this.downArrowStyle=e.downArrowStyle,this.containerClass=e.containerClass,this._init()};h.prototype={constructor:h,_init:function(){var i=this;this.$element.parent().hasClass("bootstrap-timepicker")?(this.$element.parent(".bootstrap-timepicker").find(".picker").length?this.$element.parent(".bootstrap-timepicker").find(".picker").on({"click.timepicker":t.proxy(this.showWidget,this)}):this.$element.closest(this.containerClass).find(".input-group-addon").on({"click.timepicker":t.proxy(this.showWidget,this)}),this.$element.on({"focus.timepicker":t.proxy(this.highlightUnit,this),"click.timepicker":t.proxy(this.highlightUnit,this),"keydown.timepicker":t.proxy(this.elementKeydown,this),"blur.timepicker":t.proxy(this.blurElement,this)})):this.template?this.$element.on({"focus.timepicker":t.proxy(this.showWidget,this),"click.timepicker":t.proxy(this.showWidget,this),"blur.timepicker":t.proxy(this.blurElement,this)}):this.$element.on({"focus.timepicker":t.proxy(this.highlightUnit,this),"click.timepicker":t.proxy(this.highlightUnit,this),"keydown.timepicker":t.proxy(this.elementKeydown,this),"blur.timepicker":t.proxy(this.blurElement,this)}),this.template!==!1?this.$widget=t(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on("click",t.proxy(this.widgetClick,this)):this.$widget=!1,this.showInputs&&this.$widget!==!1&&this.$widget.find("input").each(function(){t(this).on({"click.timepicker":function(){t(this).select()},"keydown.timepicker":t.proxy(i.widgetKeydown,i)})}),this.setDefaultTime(this.defaultTime)},blurElement:function(){this.highlightedUnit=s,this.updateFromElementVal()},decrementHour:function(){if(this.showMeridian)if(1===this.hour)this.hour=12;else{if(12===this.hour)return this.hour--,this.toggleMeridian();if(0===this.hour)return this.hour=11,this.toggleMeridian();this.hour--}else 0===this.hour?this.hour=23:this.hour--;this.update()},decrementMinute:function(t){var i;i=t?this.minute-t:this.minute-this.minuteStep,0>i?(this.decrementHour(),this.minute=i+60):this.minute=i,this.update()},decrementSecond:function(){var t=this.second-this.secondStep;0>t?(this.decrementMinute(!0),this.second=t+60):this.second=t,this.update()},elementKeydown:function(t){switch(t.keyCode){case 9:switch(this.updateFromElementVal(),this.highlightedUnit){case"hour":t.preventDefault(),this.highlightNextUnit();break;case"minute":(this.showMeridian||this.showSeconds)&&(t.preventDefault(),this.highlightNextUnit());break;case"second":this.showMeridian&&(t.preventDefault(),this.highlightNextUnit())}break;case 27:this.updateFromElementVal();break;case 37:t.preventDefault(),this.highlightPrevUnit(),this.updateFromElementVal();break;case 38:switch(t.preventDefault(),this.highlightedUnit){case"hour":this.incrementHour(),this.highlightHour();break;case"minute":this.incrementMinute(),this.highlightMinute();break;case"second":this.incrementSecond(),this.highlightSecond();break;case"meridian":this.toggleMeridian(),this.highlightMeridian()}break;case 39:t.preventDefault(),this.updateFromElementVal(),this.highlightNextUnit();break;case 40:switch(t.preventDefault(),this.highlightedUnit){case"hour":this.decrementHour(),this.highlightHour();break;case"minute":this.decrementMinute(),this.highlightMinute();break;case"second":this.decrementSecond(),this.highlightSecond();break;case"meridian":this.toggleMeridian(),this.highlightMeridian()}}},formatTime:function(t,i,e,s){return t=10>t?"0"+t:t,i=10>i?"0"+i:i,e=10>e?"0"+e:e,t+":"+i+(this.showSeconds?":"+e:"")+(this.showMeridian?" "+s:"")},getCursorPosition:function(){var t=this.$element.get(0);if("selectionStart"in t)return t.selectionStart;if(e.selection){t.focus();var i=e.selection.createRange(),s=e.selection.createRange().text.length;return i.moveStart("character",-t.value.length),i.text.length-s}},getTemplate:function(){var t,i,e,s,h,n;switch(this.showInputs?(i='',e='',s='',h=''):(i='',e='',s='',h=''),n=''+(this.showSeconds?'':"")+(this.showMeridian?'':"")+" "+(this.showSeconds?'":"")+(this.showMeridian?'":"")+''+(this.showSeconds?'':"")+(this.showMeridian?'':"")+"
   
"+i+' :'+e+":'+s+" '+h+"
  
",this.template){case"modal":t='';break;case"dropdown":t='"}return t},getTime:function(){return this.formatTime(this.hour,this.minute,this.second,this.meridian)},hideWidget:function(){this.isOpen!==!1&&(this.showInputs&&this.updateFromWidgetInputs(),this.$element.trigger({type:"hide.timepicker",time:{value:this.getTime(),hours:this.hour,minutes:this.minute,seconds:this.second,meridian:this.meridian}}),"modal"===this.template&&this.$widget.modal?this.$widget.modal("hide"):this.$widget.removeClass("open"),t(e).off("mousedown.timepicker"),this.isOpen=!1)},highlightUnit:function(){this.position=this.getCursorPosition(),this.position>=0&&this.position<=2?this.highlightHour():this.position>=3&&this.position<=5?this.highlightMinute():this.position>=6&&this.position<=8?this.showSeconds?this.highlightSecond():this.highlightMeridian():this.position>=9&&this.position<=11&&this.highlightMeridian()},highlightNextUnit:function(){switch(this.highlightedUnit){case"hour":this.highlightMinute();break;case"minute":this.showSeconds?this.highlightSecond():this.showMeridian?this.highlightMeridian():this.highlightHour();break;case"second":this.showMeridian?this.highlightMeridian():this.highlightHour();break;case"meridian":this.highlightHour()}},highlightPrevUnit:function(){switch(this.highlightedUnit){case"hour":this.highlightMeridian();break;case"minute":this.highlightHour();break;case"second":this.highlightMinute();break;case"meridian":this.showSeconds?this.highlightSecond():this.highlightMinute()}},highlightHour:function(){var t=this.$element.get(0);this.highlightedUnit="hour",t.setSelectionRange&&setTimeout(function(){t.setSelectionRange(0,2)},0)},highlightMinute:function(){var t=this.$element.get(0);this.highlightedUnit="minute",t.setSelectionRange&&setTimeout(function(){t.setSelectionRange(3,5)},0)},highlightSecond:function(){var t=this.$element.get(0);this.highlightedUnit="second",t.setSelectionRange&&setTimeout(function(){t.setSelectionRange(6,8)},0)},highlightMeridian:function(){var t=this.$element.get(0);this.highlightedUnit="meridian",t.setSelectionRange&&(this.showSeconds?setTimeout(function(){t.setSelectionRange(9,11)},0):setTimeout(function(){t.setSelectionRange(6,8)},0))},incrementHour:function(){if(this.showMeridian){if(11===this.hour)return this.hour++,this.toggleMeridian();12===this.hour&&(this.hour=0)}return 23===this.hour?void(this.hour=0):(this.hour++,void this.update())},incrementMinute:function(t){var i;i=t?this.minute+t:this.minute+this.minuteStep-this.minute%this.minuteStep,i>59?(this.incrementHour(),this.minute=i-60):this.minute=i,this.update()},incrementSecond:function(){var t=this.second+this.secondStep-this.second%this.secondStep;t>59?(this.incrementMinute(!0),this.second=t-60):this.second=t,this.update()},remove:function(){t("document").off(".timepicker"),this.$widget&&this.$widget.remove(),delete this.$element.data().timepicker},setDefaultTime:function(t){if(this.$element.val())this.updateFromElementVal();else if("current"===t){var i=new Date,e=i.getHours(),s=Math.floor(i.getMinutes()/this.minuteStep)*this.minuteStep,h=Math.floor(i.getSeconds()/this.secondStep)*this.secondStep,n="AM";this.showMeridian&&(0===e?e=12:e>=12?(e>12&&(e-=12),n="PM"):n="AM"),this.hour=e,this.minute=s,this.second=h,this.meridian=n,this.update(!0)}else t===!1?(this.hour=0,this.minute=0,this.second=0,this.meridian="AM"):this.setTime(t)},setTime:function(t){var i,e;this.showMeridian?(i=t.split(" "),e=i[0].split(":"),this.meridian=i[1]):e=t.split(":"),this.hour=parseInt(e[0],10),this.minute=parseInt(e[1],10),this.second=parseInt(e[2],10),isNaN(this.hour)&&(this.hour=0),isNaN(this.minute)&&(this.minute=0),this.showMeridian?(this.hour>12?this.hour=12:this.hour<1&&(this.hour=12),"am"===this.meridian||"a"===this.meridian?this.meridian="AM":"pm"!==this.meridian&&"p"!==this.meridian||(this.meridian="PM"),"AM"!==this.meridian&&"PM"!==this.meridian&&(this.meridian="AM")):this.hour>=24?this.hour=23:this.hour<0&&(this.hour=0),this.minute<0?this.minute=0:this.minute>=60&&(this.minute=59),this.showSeconds&&(isNaN(this.second)?this.second=0:this.second<0?this.second=0:this.second>=60&&(this.second=59)),this.update()},showWidget:function(){if(!this.isOpen&&!this.$element.is(":disabled")){var i=this;t(e).on("mousedown.timepicker",function(e){0===t(e.target).closest(".bootstrap-timepicker-widget").length&&i.hideWidget()}),this.$element.trigger({type:"show.timepicker",time:{value:this.getTime(),hours:this.hour,minutes:this.minute,seconds:this.second,meridian:this.meridian}}),this.disableFocus&&this.$element.blur(),this.updateFromElementVal(),"modal"===this.template&&this.$widget.modal?this.$widget.modal("show").on("hidden",t.proxy(this.hideWidget,this)):this.isOpen===!1&&this.$widget.addClass("open"),this.isOpen=!0}},toggleMeridian:function(){this.meridian="AM"===this.meridian?"PM":"AM",this.update()},update:function(t){this.$element.trigger({type:"changeTime.timepicker",time:{value:this.getTime(),hours:this.hour,minutes:this.minute,seconds:this.second,meridian:this.meridian}}),this.updateElement(t),this.updateWidget()},updateElement:function(t){var i=this.$element;i.val(this.getTime()),t||i.change()},updateFromElementVal:function(){var t=this.$element.val();t&&this.setTime(t)},updateWidget:function(){if(this.$widget!==!1){var t=this.hour<10?"0"+this.hour:this.hour,i=this.minute<10?"0"+this.minute:this.minute,e=this.second<10?"0"+this.second:this.second;this.showInputs?(this.$widget.find("input.bootstrap-timepicker-hour").val(t),this.$widget.find("input.bootstrap-timepicker-minute").val(i),this.showSeconds&&this.$widget.find("input.bootstrap-timepicker-second").val(e),this.showMeridian&&this.$widget.find("input.bootstrap-timepicker-meridian").val(this.meridian)):(this.$widget.find("span.bootstrap-timepicker-hour").text(t),this.$widget.find("span.bootstrap-timepicker-minute").text(i),this.showSeconds&&this.$widget.find("span.bootstrap-timepicker-second").text(e),this.showMeridian&&this.$widget.find("span.bootstrap-timepicker-meridian").text(this.meridian))}},updateFromWidgetInputs:function(){if(this.$widget!==!1){var i=t("input.bootstrap-timepicker-hour",this.$widget).val()+":"+t("input.bootstrap-timepicker-minute",this.$widget).val()+(this.showSeconds?":"+t("input.bootstrap-timepicker-second",this.$widget).val():"")+(this.showMeridian?" "+t("input.bootstrap-timepicker-meridian",this.$widget).val():"");this.setTime(i)}},widgetClick:function(i){i.stopPropagation(),i.preventDefault();var e=t(i.target).closest("a").data("action");e&&this[e]()},widgetKeydown:function(i){var e=t(i.target).closest("input"),s=e.attr("name");switch(i.keyCode){case 9:if(this.showMeridian){if("meridian"===s)return this.hideWidget()}else if(this.showSeconds){if("second"===s)return this.hideWidget()}else if("minute"===s)return this.hideWidget();this.updateFromWidgetInputs();break;case 27:this.hideWidget();break;case 38:switch(i.preventDefault(),s){case"hour":this.incrementHour();break;case"minute":this.incrementMinute();break;case"second":this.incrementSecond();break;case"meridian":this.toggleMeridian()}break;case 40:switch(i.preventDefault(),s){case"hour":this.decrementHour();break;case"minute":this.decrementMinute();break;case"second":this.decrementSecond();break;case"meridian":this.toggleMeridian()}}}},t.fn.timepicker=function(i){var e=Array.apply(null,arguments);return e.shift(),this.each(function(){var s=t(this),n=s.data("timepicker"),o="object"==typeof i&&i;n||s.data("timepicker",n=new h(this,t.extend({},t.fn.timepicker.defaults,o,t(this).data()))),"string"==typeof i&&n[i].apply(n,e)})},t.fn.timepicker.defaults={defaultTime:"current",disableFocus:!1,isOpen:!1,minuteStep:15,modalBackdrop:!1,secondStep:15,showSeconds:!1,showInputs:!0,showMeridian:!0,template:"dropdown",appendWidgetTo:".bootstrap-timepicker",upArrowStyle:"glyphicon glyphicon-chevron-up",downArrowStyle:"glyphicon glyphicon-chevron-down",containerClass:"bootstrap-timepicker"},t.fn.timepicker.Constructor=h}(jQuery,window,document); -------------------------------------------------------------------------------- /src/assets/js/bootstrap-timepicker.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Timepicker Component for Twitter Bootstrap 3 | * 4 | * Improvements by: Kartik Visweswaran, Krajee.com, 2014 - 2021 5 | * 6 | * Copyright 2013 Joris de Wit 7 | * 8 | * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | (function ($, window, document, undefined) { 14 | 'use strict'; 15 | 16 | // TIMEPICKER PUBLIC CLASS DEFINITION 17 | var Timepicker = function (element, options) { 18 | this.widget = ''; 19 | this.$element = $(element); 20 | this.defaultTime = options.defaultTime; 21 | this.disableFocus = options.disableFocus; 22 | this.isOpen = options.isOpen; 23 | this.minuteStep = options.minuteStep; 24 | this.modalBackdrop = options.modalBackdrop; 25 | this.secondStep = options.secondStep; 26 | this.showInputs = options.showInputs; 27 | this.showMeridian = options.showMeridian; 28 | this.showSeconds = options.showSeconds; 29 | this.template = options.template; 30 | this.appendWidgetTo = options.appendWidgetTo; 31 | this.upArrowStyle = options.upArrowStyle; 32 | this.downArrowStyle = options.downArrowStyle; 33 | this.containerClass = options.containerClass; 34 | 35 | this._init(); 36 | }; 37 | 38 | Timepicker.prototype = { 39 | constructor: Timepicker, 40 | _init: function () { 41 | var self = this; 42 | 43 | if (this.$element.parent().hasClass('bootstrap-timepicker')) { 44 | if (this.$element.parent('.bootstrap-timepicker').find('.picker').length) { 45 | this.$element.parent('.bootstrap-timepicker').find('.picker').on({ 46 | 'click.timepicker': $.proxy(this.showWidget, this) 47 | }); 48 | } else { 49 | this.$element.closest(this.containerClass).find('.input-group-addon').on({ 50 | 'click.timepicker': $.proxy(this.showWidget, this) 51 | }); 52 | } 53 | 54 | this.$element.on({ 55 | 'focus.timepicker': $.proxy(this.highlightUnit, this), 56 | 'click.timepicker': $.proxy(this.highlightUnit, this), 57 | 'keydown.timepicker': $.proxy(this.elementKeydown, this), 58 | 'blur.timepicker': $.proxy(this.blurElement, this) 59 | }); 60 | } else { 61 | if (this.template) { 62 | this.$element.on({ 63 | 'focus.timepicker': $.proxy(this.showWidget, this), 64 | 'click.timepicker': $.proxy(this.showWidget, this), 65 | 'blur.timepicker': $.proxy(this.blurElement, this) 66 | }); 67 | } else { 68 | this.$element.on({ 69 | 'focus.timepicker': $.proxy(this.highlightUnit, this), 70 | 'click.timepicker': $.proxy(this.highlightUnit, this), 71 | 'keydown.timepicker': $.proxy(this.elementKeydown, this), 72 | 'blur.timepicker': $.proxy(this.blurElement, this) 73 | }); 74 | } 75 | } 76 | 77 | if (this.template !== false) { 78 | this.$widget = $(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on('click', $.proxy(this.widgetClick, this)); 79 | } else { 80 | this.$widget = false; 81 | } 82 | 83 | if (this.showInputs && this.$widget !== false) { 84 | this.$widget.find('input').each(function () { 85 | $(this).on({ 86 | 'click.timepicker': function () { 87 | $(this).select(); 88 | }, 89 | 'keydown.timepicker': $.proxy(self.widgetKeydown, self) 90 | }); 91 | }); 92 | } 93 | 94 | this.setDefaultTime(this.defaultTime); 95 | }, 96 | blurElement: function () { 97 | this.highlightedUnit = undefined; 98 | this.updateFromElementVal(); 99 | }, 100 | decrementHour: function () { 101 | if (this.showMeridian) { 102 | if (this.hour === 1) { 103 | this.hour = 12; 104 | } else if (this.hour === 12) { 105 | this.hour--; 106 | 107 | return this.toggleMeridian(); 108 | } else if (this.hour === 0) { 109 | this.hour = 11; 110 | 111 | return this.toggleMeridian(); 112 | } else { 113 | this.hour--; 114 | } 115 | } else { 116 | if (this.hour === 0) { 117 | this.hour = 23; 118 | } else { 119 | this.hour--; 120 | } 121 | } 122 | this.update(); 123 | }, 124 | decrementMinute: function (step) { 125 | var newVal; 126 | 127 | if (step) { 128 | newVal = this.minute - step; 129 | } else { 130 | newVal = this.minute - this.minuteStep; 131 | } 132 | 133 | if (newVal < 0) { 134 | this.decrementHour(); 135 | this.minute = newVal + 60; 136 | } else { 137 | this.minute = newVal; 138 | } 139 | this.update(); 140 | }, 141 | decrementSecond: function () { 142 | var newVal = this.second - this.secondStep; 143 | 144 | if (newVal < 0) { 145 | this.decrementMinute(true); 146 | this.second = newVal + 60; 147 | } else { 148 | this.second = newVal; 149 | } 150 | this.update(); 151 | }, 152 | elementKeydown: function (e) { 153 | switch (e.keyCode) { 154 | case 9: //tab 155 | this.updateFromElementVal(); 156 | 157 | switch (this.highlightedUnit) { 158 | case 'hour': 159 | e.preventDefault(); 160 | this.highlightNextUnit(); 161 | break; 162 | case 'minute': 163 | if (this.showMeridian || this.showSeconds) { 164 | e.preventDefault(); 165 | this.highlightNextUnit(); 166 | } 167 | break; 168 | case 'second': 169 | if (this.showMeridian) { 170 | e.preventDefault(); 171 | this.highlightNextUnit(); 172 | } 173 | break; 174 | } 175 | break; 176 | case 27: // escape 177 | this.updateFromElementVal(); 178 | break; 179 | case 37: // left arrow 180 | e.preventDefault(); 181 | this.highlightPrevUnit(); 182 | this.updateFromElementVal(); 183 | break; 184 | case 38: // up arrow 185 | e.preventDefault(); 186 | switch (this.highlightedUnit) { 187 | case 'hour': 188 | this.incrementHour(); 189 | this.highlightHour(); 190 | break; 191 | case 'minute': 192 | this.incrementMinute(); 193 | this.highlightMinute(); 194 | break; 195 | case 'second': 196 | this.incrementSecond(); 197 | this.highlightSecond(); 198 | break; 199 | case 'meridian': 200 | this.toggleMeridian(); 201 | this.highlightMeridian(); 202 | break; 203 | } 204 | break; 205 | case 39: // right arrow 206 | e.preventDefault(); 207 | this.updateFromElementVal(); 208 | this.highlightNextUnit(); 209 | break; 210 | case 40: // down arrow 211 | e.preventDefault(); 212 | switch (this.highlightedUnit) { 213 | case 'hour': 214 | this.decrementHour(); 215 | this.highlightHour(); 216 | break; 217 | case 'minute': 218 | this.decrementMinute(); 219 | this.highlightMinute(); 220 | break; 221 | case 'second': 222 | this.decrementSecond(); 223 | this.highlightSecond(); 224 | break; 225 | case 'meridian': 226 | this.toggleMeridian(); 227 | this.highlightMeridian(); 228 | break; 229 | } 230 | break; 231 | } 232 | }, 233 | formatTime: function (hour, minute, second, meridian) { 234 | hour = hour < 10 ? '0' + hour : hour; 235 | minute = minute < 10 ? '0' + minute : minute; 236 | second = second < 10 ? '0' + second : second; 237 | 238 | return hour + ':' + minute + (this.showSeconds ? ':' + second : '') + (this.showMeridian ? ' ' + meridian : ''); 239 | }, 240 | getCursorPosition: function () { 241 | var input = this.$element.get(0); 242 | 243 | if ('selectionStart' in input) {// Standard-compliant browsers 244 | 245 | return input.selectionStart; 246 | } else if (document.selection) {// IE fix 247 | input.focus(); 248 | var sel = document.selection.createRange(), 249 | selLen = document.selection.createRange().text.length; 250 | 251 | sel.moveStart('character', -input.value.length); 252 | 253 | return sel.text.length - selLen; 254 | } 255 | }, 256 | getTemplate: function () { 257 | var template, 258 | hourTemplate, 259 | minuteTemplate, 260 | secondTemplate, 261 | meridianTemplate, 262 | templateContent; 263 | 264 | if (this.showInputs) { 265 | hourTemplate = ''; 266 | minuteTemplate = ''; 267 | secondTemplate = ''; 268 | meridianTemplate = ''; 269 | } else { 270 | hourTemplate = ''; 271 | minuteTemplate = ''; 272 | secondTemplate = ''; 273 | meridianTemplate = ''; 274 | } 275 | 276 | templateContent = '' + 277 | '' + 278 | '' + 279 | '' + 280 | '' + 281 | (this.showSeconds ? 282 | '' + 283 | '' 284 | : '') + 285 | (this.showMeridian ? 286 | '' + 287 | '' 288 | : '') + 289 | '' + 290 | '' + 291 | ' ' + 292 | '' + 293 | ' ' + 294 | (this.showSeconds ? 295 | '' + 296 | '' 297 | : '') + 298 | (this.showMeridian ? 299 | '' + 300 | '' 301 | : '') + 302 | '' + 303 | '' + 304 | '' + 305 | '' + 306 | '' + 307 | (this.showSeconds ? 308 | '' + 309 | '' 310 | : '') + 311 | (this.showMeridian ? 312 | '' + 313 | '' 314 | : '') + 315 | '' + 316 | '
   
' + hourTemplate + ':' + minuteTemplate + ':' + secondTemplate + ' ' + meridianTemplate + '
  
'; 317 | 318 | switch (this.template) { 319 | case 'modal': 320 | template = ''; 332 | break; 333 | case 'dropdown': 334 | template = ''; 335 | break; 336 | } 337 | 338 | return template; 339 | }, 340 | getTime: function () { 341 | return this.formatTime(this.hour, this.minute, this.second, this.meridian); 342 | }, 343 | hideWidget: function () { 344 | if (this.isOpen === false) { 345 | return; 346 | } 347 | 348 | if (this.showInputs) { 349 | this.updateFromWidgetInputs(); 350 | } 351 | 352 | this.$element.trigger({ 353 | 'type': 'hide.timepicker', 354 | 'time': { 355 | 'value': this.getTime(), 356 | 'hours': this.hour, 357 | 'minutes': this.minute, 358 | 'seconds': this.second, 359 | 'meridian': this.meridian 360 | } 361 | }); 362 | 363 | if (this.template === 'modal' && this.$widget.modal) { 364 | this.$widget.modal('hide'); 365 | } else { 366 | this.$widget.removeClass('open'); 367 | } 368 | 369 | $(document).off('mousedown.timepicker'); 370 | 371 | this.isOpen = false; 372 | }, 373 | highlightUnit: function () { 374 | this.position = this.getCursorPosition(); 375 | if (this.position >= 0 && this.position <= 2) { 376 | this.highlightHour(); 377 | } else if (this.position >= 3 && this.position <= 5) { 378 | this.highlightMinute(); 379 | } else if (this.position >= 6 && this.position <= 8) { 380 | if (this.showSeconds) { 381 | this.highlightSecond(); 382 | } else { 383 | this.highlightMeridian(); 384 | } 385 | } else if (this.position >= 9 && this.position <= 11) { 386 | this.highlightMeridian(); 387 | } 388 | }, 389 | highlightNextUnit: function () { 390 | switch (this.highlightedUnit) { 391 | case 'hour': 392 | this.highlightMinute(); 393 | break; 394 | case 'minute': 395 | if (this.showSeconds) { 396 | this.highlightSecond(); 397 | } else if (this.showMeridian) { 398 | this.highlightMeridian(); 399 | } else { 400 | this.highlightHour(); 401 | } 402 | break; 403 | case 'second': 404 | if (this.showMeridian) { 405 | this.highlightMeridian(); 406 | } else { 407 | this.highlightHour(); 408 | } 409 | break; 410 | case 'meridian': 411 | this.highlightHour(); 412 | break; 413 | } 414 | }, 415 | highlightPrevUnit: function () { 416 | switch (this.highlightedUnit) { 417 | case 'hour': 418 | this.highlightMeridian(); 419 | break; 420 | case 'minute': 421 | this.highlightHour(); 422 | break; 423 | case 'second': 424 | this.highlightMinute(); 425 | break; 426 | case 'meridian': 427 | if (this.showSeconds) { 428 | this.highlightSecond(); 429 | } else { 430 | this.highlightMinute(); 431 | } 432 | break; 433 | } 434 | }, 435 | highlightHour: function () { 436 | var $element = this.$element.get(0); 437 | 438 | this.highlightedUnit = 'hour'; 439 | 440 | if ($element.setSelectionRange) { 441 | setTimeout(function () { 442 | $element.setSelectionRange(0, 2); 443 | }, 0); 444 | } 445 | }, 446 | highlightMinute: function () { 447 | var $element = this.$element.get(0); 448 | 449 | this.highlightedUnit = 'minute'; 450 | 451 | if ($element.setSelectionRange) { 452 | setTimeout(function () { 453 | $element.setSelectionRange(3, 5); 454 | }, 0); 455 | } 456 | }, 457 | highlightSecond: function () { 458 | var $element = this.$element.get(0); 459 | 460 | this.highlightedUnit = 'second'; 461 | 462 | if ($element.setSelectionRange) { 463 | setTimeout(function () { 464 | $element.setSelectionRange(6, 8); 465 | }, 0); 466 | } 467 | }, 468 | highlightMeridian: function () { 469 | var $element = this.$element.get(0); 470 | 471 | this.highlightedUnit = 'meridian'; 472 | 473 | if ($element.setSelectionRange) { 474 | if (this.showSeconds) { 475 | setTimeout(function () { 476 | $element.setSelectionRange(9, 11); 477 | }, 0); 478 | } else { 479 | setTimeout(function () { 480 | $element.setSelectionRange(6, 8); 481 | }, 0); 482 | } 483 | } 484 | }, 485 | incrementHour: function () { 486 | if (this.showMeridian) { 487 | if (this.hour === 11) { 488 | this.hour++; 489 | return this.toggleMeridian(); 490 | } else if (this.hour === 12) { 491 | this.hour = 0; 492 | } 493 | } 494 | if (this.hour === 23) { 495 | this.hour = 0; 496 | 497 | return; 498 | } 499 | this.hour++; 500 | this.update(); 501 | }, 502 | incrementMinute: function (step) { 503 | var newVal; 504 | 505 | if (step) { 506 | newVal = this.minute + step; 507 | } else { 508 | newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep); 509 | } 510 | 511 | if (newVal > 59) { 512 | this.incrementHour(); 513 | this.minute = newVal - 60; 514 | } else { 515 | this.minute = newVal; 516 | } 517 | this.update(); 518 | }, 519 | incrementSecond: function () { 520 | var newVal = this.second + this.secondStep - (this.second % this.secondStep); 521 | 522 | if (newVal > 59) { 523 | this.incrementMinute(true); 524 | this.second = newVal - 60; 525 | } else { 526 | this.second = newVal; 527 | } 528 | this.update(); 529 | }, 530 | remove: function () { 531 | $('document').off('.timepicker'); 532 | if (this.$widget) { 533 | this.$widget.remove(); 534 | } 535 | delete this.$element.data().timepicker; 536 | }, 537 | setDefaultTime: function (defaultTime) { 538 | if (!this.$element.val()) { 539 | if (defaultTime === 'current') { 540 | var dTime = new Date(), 541 | hours = dTime.getHours(), 542 | minutes = Math.floor(dTime.getMinutes() / this.minuteStep) * this.minuteStep, 543 | seconds = Math.floor(dTime.getSeconds() / this.secondStep) * this.secondStep, 544 | meridian = 'AM'; 545 | 546 | if (this.showMeridian) { 547 | if (hours === 0) { 548 | hours = 12; 549 | } else if (hours >= 12) { 550 | if (hours > 12) { 551 | hours = hours - 12; 552 | } 553 | meridian = 'PM'; 554 | } else { 555 | meridian = 'AM'; 556 | } 557 | } 558 | 559 | this.hour = hours; 560 | this.minute = minutes; 561 | this.second = seconds; 562 | this.meridian = meridian; 563 | 564 | this.update(true); 565 | 566 | } else if (defaultTime === false) { 567 | this.hour = 0; 568 | this.minute = 0; 569 | this.second = 0; 570 | this.meridian = 'AM'; 571 | } else { 572 | this.setTime(defaultTime); 573 | } 574 | } else { 575 | this.updateFromElementVal(); 576 | } 577 | }, 578 | setTime: function (time) { 579 | var arr, 580 | timeArray; 581 | 582 | if (this.showMeridian) { 583 | arr = time.split(' '); 584 | timeArray = arr[0].split(':'); 585 | this.meridian = arr[1]; 586 | } else { 587 | timeArray = time.split(':'); 588 | } 589 | 590 | this.hour = parseInt(timeArray[0], 10); 591 | this.minute = parseInt(timeArray[1], 10); 592 | this.second = parseInt(timeArray[2], 10); 593 | 594 | if (isNaN(this.hour)) { 595 | this.hour = 0; 596 | } 597 | if (isNaN(this.minute)) { 598 | this.minute = 0; 599 | } 600 | 601 | if (this.showMeridian) { 602 | if (this.hour > 12) { 603 | this.hour = 12; 604 | } else if (this.hour < 1) { 605 | this.hour = 12; 606 | } 607 | 608 | if (this.meridian === 'am' || this.meridian === 'a') { 609 | this.meridian = 'AM'; 610 | } else if (this.meridian === 'pm' || this.meridian === 'p') { 611 | this.meridian = 'PM'; 612 | } 613 | 614 | if (this.meridian !== 'AM' && this.meridian !== 'PM') { 615 | this.meridian = 'AM'; 616 | } 617 | } else { 618 | if (this.hour >= 24) { 619 | this.hour = 23; 620 | } else if (this.hour < 0) { 621 | this.hour = 0; 622 | } 623 | } 624 | 625 | if (this.minute < 0) { 626 | this.minute = 0; 627 | } else if (this.minute >= 60) { 628 | this.minute = 59; 629 | } 630 | 631 | if (this.showSeconds) { 632 | if (isNaN(this.second)) { 633 | this.second = 0; 634 | } else if (this.second < 0) { 635 | this.second = 0; 636 | } else if (this.second >= 60) { 637 | this.second = 59; 638 | } 639 | } 640 | 641 | this.update(); 642 | }, 643 | showWidget: function () { 644 | if (this.isOpen) { 645 | return; 646 | } 647 | 648 | if (this.$element.is(':disabled')) { 649 | return; 650 | } 651 | 652 | var self = this; 653 | $(document).on('mousedown.timepicker', function (e) { 654 | // Clicked outside the timepicker, hide it 655 | if ($(e.target).closest('.bootstrap-timepicker-widget').length === 0) { 656 | self.hideWidget(); 657 | } 658 | }); 659 | 660 | this.$element.trigger({ 661 | 'type': 'show.timepicker', 662 | 'time': { 663 | 'value': this.getTime(), 664 | 'hours': this.hour, 665 | 'minutes': this.minute, 666 | 'seconds': this.second, 667 | 'meridian': this.meridian 668 | } 669 | }); 670 | 671 | if (this.disableFocus) { 672 | this.$element.blur(); 673 | } 674 | 675 | this.updateFromElementVal(); 676 | 677 | if (this.template === 'modal' && this.$widget.modal) { 678 | this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this)); 679 | } else { 680 | if (this.isOpen === false) { 681 | this.$widget.addClass('open'); 682 | } 683 | } 684 | 685 | this.isOpen = true; 686 | }, 687 | toggleMeridian: function () { 688 | this.meridian = this.meridian === 'AM' ? 'PM' : 'AM'; 689 | this.update(); 690 | }, 691 | update: function (skipChange) { 692 | this.$element.trigger({ 693 | 'type': 'changeTime.timepicker', 694 | 'time': { 695 | 'value': this.getTime(), 696 | 'hours': this.hour, 697 | 'minutes': this.minute, 698 | 'seconds': this.second, 699 | 'meridian': this.meridian 700 | } 701 | }); 702 | 703 | this.updateElement(skipChange); 704 | this.updateWidget(); 705 | }, 706 | updateElement: function (skipChange) { 707 | var $el = this.$element; 708 | $el.val(this.getTime()); 709 | if (!skipChange) { 710 | $el.change(); 711 | } 712 | }, 713 | updateFromElementVal: function () { 714 | var val = this.$element.val(); 715 | 716 | if (val) { 717 | this.setTime(val); 718 | } 719 | }, 720 | updateWidget: function () { 721 | if (this.$widget === false) { 722 | return; 723 | } 724 | 725 | var hour = this.hour < 10 ? '0' + this.hour : this.hour, 726 | minute = this.minute < 10 ? '0' + this.minute : this.minute, 727 | second = this.second < 10 ? '0' + this.second : this.second; 728 | 729 | if (this.showInputs) { 730 | this.$widget.find('input.bootstrap-timepicker-hour').val(hour); 731 | this.$widget.find('input.bootstrap-timepicker-minute').val(minute); 732 | 733 | if (this.showSeconds) { 734 | this.$widget.find('input.bootstrap-timepicker-second').val(second); 735 | } 736 | if (this.showMeridian) { 737 | this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian); 738 | } 739 | } else { 740 | this.$widget.find('span.bootstrap-timepicker-hour').text(hour); 741 | this.$widget.find('span.bootstrap-timepicker-minute').text(minute); 742 | 743 | if (this.showSeconds) { 744 | this.$widget.find('span.bootstrap-timepicker-second').text(second); 745 | } 746 | if (this.showMeridian) { 747 | this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian); 748 | } 749 | } 750 | }, 751 | updateFromWidgetInputs: function () { 752 | if (this.$widget === false) { 753 | return; 754 | } 755 | var time = $('input.bootstrap-timepicker-hour', this.$widget).val() + ':' + 756 | $('input.bootstrap-timepicker-minute', this.$widget).val() + 757 | (this.showSeconds ? ':' + $('input.bootstrap-timepicker-second', this.$widget).val() : '') + 758 | (this.showMeridian ? ' ' + $('input.bootstrap-timepicker-meridian', this.$widget).val() : ''); 759 | 760 | this.setTime(time); 761 | }, 762 | widgetClick: function (e) { 763 | e.stopPropagation(); 764 | e.preventDefault(); 765 | 766 | var action = $(e.target).closest('a').data('action'); 767 | if (action) { 768 | this[action](); 769 | } 770 | }, 771 | widgetKeydown: function (e) { 772 | var $input = $(e.target).closest('input'), 773 | name = $input.attr('name'); 774 | 775 | switch (e.keyCode) { 776 | case 9: //tab 777 | if (this.showMeridian) { 778 | if (name === 'meridian') { 779 | return this.hideWidget(); 780 | } 781 | } else { 782 | if (this.showSeconds) { 783 | if (name === 'second') { 784 | return this.hideWidget(); 785 | } 786 | } else { 787 | if (name === 'minute') { 788 | return this.hideWidget(); 789 | } 790 | } 791 | } 792 | 793 | this.updateFromWidgetInputs(); 794 | break; 795 | case 27: // escape 796 | this.hideWidget(); 797 | break; 798 | case 38: // up arrow 799 | e.preventDefault(); 800 | switch (name) { 801 | case 'hour': 802 | this.incrementHour(); 803 | break; 804 | case 'minute': 805 | this.incrementMinute(); 806 | break; 807 | case 'second': 808 | this.incrementSecond(); 809 | break; 810 | case 'meridian': 811 | this.toggleMeridian(); 812 | break; 813 | } 814 | break; 815 | case 40: // down arrow 816 | e.preventDefault(); 817 | switch (name) { 818 | case 'hour': 819 | this.decrementHour(); 820 | break; 821 | case 'minute': 822 | this.decrementMinute(); 823 | break; 824 | case 'second': 825 | this.decrementSecond(); 826 | break; 827 | case 'meridian': 828 | this.toggleMeridian(); 829 | break; 830 | } 831 | break; 832 | } 833 | } 834 | }; 835 | 836 | 837 | //TIMEPICKER PLUGIN DEFINITION 838 | $.fn.timepicker = function (option) { 839 | var args = Array.apply(null, arguments); 840 | args.shift(); 841 | return this.each(function () { 842 | var $this = $(this), 843 | data = $this.data('timepicker'), 844 | options = typeof option === 'object' && option; 845 | 846 | if (!data) { 847 | $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data())))); 848 | } 849 | 850 | if (typeof option === 'string') { 851 | data[option].apply(data, args); 852 | } 853 | }); 854 | }; 855 | 856 | $.fn.timepicker.defaults = { 857 | defaultTime: 'current', 858 | disableFocus: false, 859 | isOpen: false, 860 | minuteStep: 15, 861 | modalBackdrop: false, 862 | secondStep: 15, 863 | showSeconds: false, 864 | showInputs: true, 865 | showMeridian: true, 866 | template: 'dropdown', 867 | appendWidgetTo: '.bootstrap-timepicker', 868 | upArrowStyle: 'glyphicon glyphicon-chevron-up', 869 | downArrowStyle: 'glyphicon glyphicon-chevron-down', 870 | containerClass: 'bootstrap-timepicker' 871 | }; 872 | 873 | $.fn.timepicker.Constructor = Timepicker; 874 | 875 | })(jQuery, window, document); 876 | --------------------------------------------------------------------------------