The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .bowerrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── _config.yml
├── _layouts
    └── default.html
├── assets
    ├── bootstrap
    │   └── css
    │   │   └── bootstrap-responsive.css
    ├── ico
    │   └── favicon.ico
    ├── images
    │   └── bg.png
    ├── prettify
    │   ├── prettify.css
    │   └── prettify.js
    └── pygments.css
├── bower.json
├── composer.json
├── css
    └── timepicker.less
├── index.html
├── js
    └── bootstrap-timepicker.js
├── package.json
└── spec
    └── js
        ├── KeyboardEventsSpec.js
        ├── MouseEventsSpec.js
        ├── TimepickerSpec.js
        ├── fixtures
            └── timepicker.html
        └── helpers
            ├── SpecHelper.js
            └── jasmine-jquery.js


/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 |   "directory" : "spec/js/libs/"
3 | }
4 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | *.swp
 2 | _site/
 3 | .grunt
 4 | node_modules
 5 | _site
 6 | _SpecRunner.html
 7 | 
 8 | # build artifacts
 9 | js/bootstrap-timepicker.min.js
10 | css/bootstrap-timepicker.css
11 | css/bootstrap-timepicker.min.css
12 | 
13 | # bower artifacts for test
14 | spec/js/libs
15 | 


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /.travis.yml
2 | 
3 | /spec
4 | /Gruntfile.js
5 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 |   - "0.12"
4 | branches:
5 |   only:
6 |     - /.*/
7 | sudo: false
8 | 


--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
 1 | # Changelog
 2 | 
 3 | All notable changes will be documented in this file.  This project
 4 | sort of conforms to Semantic Versioning. Since we're still pre-1.0,
 5 | it's like the Wild West up in here!
 6 | 
 7 | ## Unreleased
 8 | ### Added (not started)
 9 | - Still planning out how to include i18n data and functionality.
10 | 
11 | ### Deprecated (not started)
12 | - Incorrect usage of the word "meridian" will be deprecated. It should
13 |   be "meridiem".
14 | - `showWidgetOnAddonClick`'s current behavior is not intuitive. Clicking
15 |   the input addon should _toggle_ the widget instead of showing it.
16 | 
17 | ## 0.5.2 - 2016-01-02
18 | ### Added
19 | - Tabbing out of the timepicker widget will now close it.
20 | - You can specify your own icon classes. See docs for the option.
21 | 
22 | ### Changed
23 | - Cleaned up `package.json` and `bower.json` files. The npm/bower package
24 |   should be cleaner now.
25 | - `timepicker.less` now lives in the `css/` directory of the package.
26 | - bootstrap-timepicker now uses the latest minor releases for jQuery 2 and
27 |   Bootstrap 3
28 | 
29 | ### Fixed
30 | - Fixed bad interaction between `setTime("12:00 AM")` and `showMeridian`
31 | - Various documentation issues were fixed.
32 | 
33 | ## 0.5.1 - 2015-08-06
34 | ### Changed
35 | - Critical fix (#279) for bootstrap initialization. If you happened to
36 |   list your timepicker's classes in an order other than "input-group
37 |   bootstrap-timepicker", you'd be out of luck. Now we use jQuery's
38 |   `hasClass` method correctly. Yay!
39 | 
40 | ## 0.5 - 2015-07-31
41 | ### Changed
42 | - Bootstrap 3 support. No more Bootstrap 2 support.
43 | - setTime sets time better
44 | - more tests, and they exercise Bootstrap 3 support!
45 | - snapToStep is a new option, off by default, which snaps times to the
46 |   nearest step or overflows to 0 if it would otherwise snap to 60 or
47 |   more.
48 | - explicitMode is a new option, off by default, which lets you leave
49 |   out colons when typing times.
50 | - shift+tab now correctly moves the cursor to the previously
51 |   highlighted unit, and blurs the timepicker when expected.
52 | - We have cut out significant amounts of old cruft from the
53 |   repository.
54 | - Minified/Uglified code is no longer kept in the repo. Please
55 |   download a release tarball or zip file to get the compiled and
56 |   minified CSS and Javascript files.
57 | 


--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
  1 | module.exports = function(grunt) {
  2 |   'use strict';
  3 | 
  4 |   grunt.loadNpmTasks('grunt-contrib-jshint');
  5 |   grunt.loadNpmTasks('grunt-contrib-jasmine');
  6 |   grunt.loadNpmTasks('grunt-contrib-less');
  7 |   grunt.loadNpmTasks('grunt-contrib-uglify');
  8 |   grunt.loadNpmTasks('grunt-contrib-watch');
  9 |   grunt.loadNpmTasks('grunt-bump');
 10 | 
 11 |   grunt.initConfig({
 12 |     'pkg': grunt.file.readJSON('package.json'),
 13 |     'meta': {
 14 |       project: '<%= pkg.name %>',
 15 |       version: '<%= pkg.version %>'
 16 |     },
 17 |     'bump': {
 18 |       options: {
 19 |         push: false,
 20 |         files: ['package.json', 'bower.json', 'composer.json'],
 21 |         commitFiles: ['package.json', 'bower.json', 'composer.json']
 22 |       }
 23 |     },
 24 |     'jasmine': {
 25 |       build: {
 26 |         src : ['spec/js/libs/jquery/dist/jquery.min.js', 'spec/js/libs/bootstrap/dist/js/bootstrap.min.js', 'spec/js/libs/autotype/index.js', 'js/bootstrap-timepicker.js'],
 27 |         options: {
 28 |           specs : 'spec/js/*Spec.js',
 29 |           helpers : 'spec/js/helpers/*.js',
 30 |           timeout : 100
 31 |         }
 32 |       }
 33 |     },
 34 |     'jshint': {
 35 |       options: {
 36 |         browser: true,
 37 |         camelcase: true,
 38 |         curly: true,
 39 |         eqeqeq: true,
 40 |         eqnull: true,
 41 |         immed: true,
 42 |         indent: 2,
 43 |         latedef: true,
 44 |         newcap: true,
 45 |         noarg: true,
 46 |         quotmark: true,
 47 |         sub: true,
 48 |         strict: true,
 49 |         trailing: true,
 50 |         undef: true,
 51 |         unused: true,
 52 |         laxcomma: true,
 53 |         white: false,
 54 |         globals: {
 55 |           jQuery: true,
 56 |           $: true,
 57 |           expect: true,
 58 |           it: true,
 59 |           beforeEach: true,
 60 |           afterEach: true,
 61 |           describe: true,
 62 |           loadFixtures: true,
 63 |           console: true,
 64 |           module: true
 65 |         }
 66 |       },
 67 |       files: ['js/bootstrap-timepicker.js', 'Gruntfile.js', 'package.json', 'spec/js/*Spec.js']
 68 |     },
 69 |     'less': {
 70 |       dev: {
 71 |         options: {
 72 |           paths: ['css']
 73 |         },
 74 |         files: {
 75 |           'css/bootstrap-timepicker.css': ['css/*.less']
 76 |         }
 77 |       },
 78 |       prod: {
 79 |         options: {
 80 |           paths: ['css'],
 81 |           yuicompress: true
 82 |         },
 83 |         files: {
 84 |           'css/bootstrap-timepicker.min.css': ['css/*.less']
 85 |         }
 86 |       }
 87 |     },
 88 |     'uglify': {
 89 |       options: {
 90 |         banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n' +
 91 |           '* http://jdewit.github.com/bootstrap-timepicker \n' +
 92 |           '* Copyright (c) <%= grunt.template.today("yyyy") %> Joris de Wit and bootstrap-timepicker contributors \n' +
 93 |           '* MIT License \n' +
 94 |           '*/',
 95 |         report: 'min'
 96 |       },
 97 |       build: {
 98 |         src: ['<banner:meta.banner>','js/<%= pkg.name %>.js'],
 99 |         dest: 'js/<%= pkg.name %>.min.js'
100 |       }
101 |     },
102 |     'watch': {
103 |       js: {
104 |         files: ['js/bootstrap-timepicker.js', 'spec/js/*Spec.js'],
105 |         tasks: ['jshint', 'jasmine'],
106 |         options: {
107 |           livereload: true
108 |         }
109 |       },
110 |       less: {
111 |         files: ['css/timepicker.less'],
112 |         tasks: ['less:dev'],
113 |         options: {
114 |           livereload: true
115 |         }
116 |       }
117 |     }
118 |   });
119 | 
120 |   grunt.registerTask('default', ['jshint', 'jasmine', 'less:dev']);
121 |   grunt.registerTask('test', ['jasmine', 'jshint']);
122 |   grunt.registerTask('compile', ['jshint', 'jasmine', 'uglify', 'less:prod']);
123 |   grunt.registerTask('compileAll', ['jshint', 'jasmine', 'uglify', 'less:dev', 'less:prod']);
124 | };
125 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT license
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is furnished
 8 | to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 | 
21 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | Timepicker for Twitter Bootstrap
 2 | ================================
 3 | 
 4 | WARNING: This project is no longer maintained.
 5 | 
 6 | [![Build Status](https://travis-ci.org/jdewit/bootstrap-timepicker.svg?branch=gh-pages)](https://travis-ci.org/jdewit/bootstrap-timepicker)
 7 | 
 8 | A simple timepicker component for Twitter Bootstrap.
 9 | 
10 | Status
11 | ======
12 | Please take a look at the `CHANGELOG.md` and the issues tab for issues we're
13 | working on and their relative priorities.
14 | 
15 | Installation
16 | ============
17 | 
18 | This project is registered as a <a href="http://bower.io">Bower</a> package,
19 | and can be installed with the following command:
20 | 
21 | ```bash
22 | bower install bootstrap-timepicker
23 | ```
24 | 
25 | You can also download our latest release (and any previous release) 
26 | <a href="https://github.com/jdewit/bootstrap-timepicker/releases">here</a>.
27 | 
28 | Demos & Documentation
29 | =====================
30 | 
31 | View <a href="http://jdewit.github.com/bootstrap-timepicker">demos & documentation</a>.
32 | 
33 | Support
34 | =======
35 | 
36 | If you make money using this timepicker, please consider 
37 | supporting its development.
38 | 
39 | <a href="http://www.pledgie.com/campaigns/19125"><img alt="Click here to support bootstrap-timepicker!" src="http://www.pledgie.com/campaigns/19125.png?skin_name=chrome" border="0" target="_blank"/></a> <a class="FlattrButton" style="display:none;" rev="flattr;button:compact;" href="http://jdewit.github.com/bootstrap-timepicker"></a> <noscript><a href="http://flattr.com/thing/1116513/Bootstrap-Timepicker" target="_blank"> <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a></noscript>
40 | 
41 | Contributing
42 | ============
43 | 
44 | 1. Install <a href="www.nodejs.org">NodeJS</a> and <a href="www.npmjs.org">Node Package Manager</a>.
45 | 
46 | 2. Install packages
47 | 
48 | ```bash
49 | npm install
50 | ```
51 | 
52 | 3. Use <a href="https://github.com/twitter/bower">Bower</a> to get the dev dependencies.
53 | 
54 | ```bash 
55 | bower install
56 | ```
57 | 
58 | 4. Use <a href="www.gruntjs.com">Grunt</a> to run tests, compress assets, etc. 
59 | 
60 | ```bash 
61 | grunt test // run jshint and jasmine tests
62 | grunt watch // run jsHint and Jasmine tests whenever a file is changed
63 | grunt compile // minify the js and css files
64 | ```
65 | 
66 | - Please make it easy on me by covering any new features or issues 
67 |   with <a href="http://pivotal.github.com/jasmine">Jasmine</a> tests.
68 | - If your changes need documentation, please take the time to update the docs.
69 | 
70 | Acknowledgements
71 | ================
72 | 
73 | Thanks to everyone who have given feedback and submitted pull requests. A 
74 | list of all the contributors can be found <a href="https://github.com/jdewit/bootstrap-timepicker/graphs/contributors">here</a>.
75 | 
76 | Special thanks to @eternicode and his <a href="https://github.com/eternicode/bootstrap-datepicker">Twitter Datepicker</a> for inspiration.
77 | 


--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | permalink: /:title
2 | auto: true
3 | server: true
4 | pygments: true
5 | 


--------------------------------------------------------------------------------
/_layouts/default.html:
--------------------------------------------------------------------------------
  1 | <!DOCTYPE html>
  2 | <html>
  3 | <head>
  4 |     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5 |     <meta content="width=device-width, initial-scale=1.0" name="viewport">
  6 | 
  7 |     <meta content="A timepicker component for Twitter Bootstrap" name="description">
  8 |     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" type="text/css" media="screen" />
  9 |     <link href="assets/pygments.css" type="text/css" rel="stylesheet" />
 10 |     <link href="assets/prettify/prettify.css" type="text/css" rel="stylesheet" />
 11 |     <link rel="stylesheet/less" type="text/css" href="css/timepicker.less" />
 12 |     <style type="text/css">
 13 |         body {
 14 |             padding-top: 50px;
 15 |             font-size: 16px;
 16 |             background: url('assets/images/bg.png');
 17 |         }
 18 |         hr {
 19 |             border-color: #ddd;
 20 |         }
 21 |         hr.soften {
 22 |             background-image: -moz-linear-gradient(left center , transparent, rgba(0, 0, 0, 0.1), transparent);
 23 |             border: 0 none;
 24 |             height: 1px;
 25 |             margin: 15px 0;
 26 |         }
 27 |         .input-group {
 28 |             vertical-align: middle;
 29 |             margin-bottom: 10px;
 30 |         }
 31 |         .jumbotron {
 32 |             color: #ffffff;
 33 |             text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
 34 |             background-color: #363636;
 35 |             *background-color: #222222;
 36 |             background-image: -moz-linear-gradient(top, #444444, #222222);
 37 |             background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
 38 |             background-image: -webkit-linear-gradient(top, #444444, #222222);
 39 |             background-image: -o-linear-gradient(top, #444444, #222222);
 40 |             background-image: linear-gradient(to bottom, #444444, #222222);
 41 |             background-repeat: repeat-x;
 42 |             border-color: #222222 #222222 #000000;
 43 |             border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
 44 |             filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
 45 |             filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
 46 |         }
 47 | 
 48 |         @media (max-width: 480px) {
 49 |             .jumbotron h1 {
 50 |                 font-size: 50px;
 51 |             }
 52 |             table.options-table td:nth-child(3), table th:nth-child(3) {
 53 |                 display: none;
 54 |             }
 55 |         }
 56 |         .center {
 57 |             text-align: center;
 58 |         }
 59 |         .span4.center {
 60 |             padding-bottom: 20px;
 61 |         }
 62 | 
 63 |     </style>
 64 |     <title>{{ page.title }}</title>
 65 | <script type="text/javascript">
 66 | /* <![CDATA[ */
 67 |     (function() {
 68 |         var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
 69 |         s.type = 'text/javascript';
 70 |         s.async = true;
 71 |         s.src = 'https://api.flattr.com/js/0.6/load.js?mode=auto';
 72 |         t.parentNode.insertBefore(s, t);
 73 |     })();
 74 | /* ]]> */</script>
 75 | </head>
 76 | <body onload="prettyPrint()" data-target=".subnav" data-spy="scroll" data-twttr-rendered="true" >
 77 |     <nav class="navbar navbar-inverse navbar-fixed-top">
 78 |       <div class="container">
 79 |         <div class="navbar-header">
 80 |           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".nav-collapse">
 81 |             <span class="glyphicon glyphicon-bar"></span>
 82 |             <span class="glyphicon glyphicon-bar"></span>
 83 |             <span class="glyphicon glyphicon-bar"></span>
 84 |           </button>
 85 |           <a class="brand" href="http://jdewit.github.com/bootstrap-timepicker">Bootstrap-Timepicker</a>
 86 |           <div class="nav-collapse collapse">
 87 |             <ul class="nav navbar-nav">
 88 |               <li><a href="index.html">Documentation</a></li>
 89 |             </ul>
 90 |           </div><!--/.nav-collapse -->
 91 |         </div>
 92 |       </div>
 93 |     </nav>
 94 |     <div class="container">
 95 |         {{ content }}
 96 |     </div> <!-- /container -->
 97 |     <hr />
 98 | 
 99 |     <footer class="container">
100 |         <p class="pull-right"><a href="#">Back to top</a></p>
101 |         <p>Created by <a href="http://twitter.com/joris_dewit" target="_blank">Joris de Wit</a> and many <a href="https://github.com/jdewit/bootstrap-timepicker/graphs/contributors">contributors</a></p>
102 |     </footer>
103 | 
104 |     <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
105 |     <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
106 |     <script type="text/javascript" src="assets/prettify/prettify.js"></script>
107 |     <script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.1/less.min.js"></script>
108 |     <script type="text/javascript" src="js/bootstrap-timepicker.js"></script>
109 |     <script type="text/javascript">
110 |         $(document).ready(function () {
111 |             $('#timepicker1').timepicker();
112 | 
113 |             $('#timepicker2').timepicker({
114 |                 minuteStep: 1,
115 |                 template: 'modal',
116 |                 appendWidgetTo: 'body',
117 |                 showSeconds: true,
118 |                 showMeridian: false,
119 |                 defaultTime: false
120 |             });
121 | 
122 |             $('#timepicker3').timepicker({
123 |                 minuteStep: 5,
124 |                 showInputs: false,
125 |                 disableFocus: true
126 |             });
127 | 
128 |             $('#timepicker4').timepicker({
129 |                 minuteStep: 1,
130 |                 secondStep: 5,
131 |                 showInputs: false,
132 |                 modalBackdrop: true,
133 |                 showSeconds: true,
134 |                 showMeridian: false
135 |             });
136 | 
137 |             $('#timepicker5').timepicker({
138 |                 template: false,
139 |                 showInputs: false,
140 |                 minuteStep: 5
141 |             });
142 | 
143 |             setTimeout(function() {
144 |               $('#timeDisplay').text($('#timepicker1').val());
145 |             }, 100);
146 | 
147 |             $('#timepicker1').on('changeTime.timepicker', function(e) {
148 |               $('#timeDisplay').text(e.time.value);
149 |             });
150 |         });
151 |     </script>
152 | </body>
153 | </html>
154 | 


--------------------------------------------------------------------------------
/assets/bootstrap/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
   1 | /*!
   2 |  * Bootstrap Responsive v2.2.2
   3 |  *
   4 |  * Copyright 2012 Twitter, Inc
   5 |  * Licensed under the Apache License v2.0
   6 |  * http://www.apache.org/licenses/LICENSE-2.0
   7 |  *
   8 |  * Designed and built with all the love in the world @twitter by @mdo and @fat.
   9 |  */
  10 | 
  11 | @-ms-viewport {
  12 |   width: device-width;
  13 | }
  14 | 
  15 | .clearfix {
  16 |   *zoom: 1;
  17 | }
  18 | 
  19 | .clearfix:before,
  20 | .clearfix:after {
  21 |   display: table;
  22 |   line-height: 0;
  23 |   content: "";
  24 | }
  25 | 
  26 | .clearfix:after {
  27 |   clear: both;
  28 | }
  29 | 
  30 | .hide-text {
  31 |   font: 0/0 a;
  32 |   color: transparent;
  33 |   text-shadow: none;
  34 |   background-color: transparent;
  35 |   border: 0;
  36 | }
  37 | 
  38 | .input-block-level {
  39 |   display: block;
  40 |   width: 100%;
  41 |   min-height: 30px;
  42 |   -webkit-box-sizing: border-box;
  43 |      -moz-box-sizing: border-box;
  44 |           box-sizing: border-box;
  45 | }
  46 | 
  47 | .hidden {
  48 |   display: none;
  49 |   visibility: hidden;
  50 | }
  51 | 
  52 | .visible-phone {
  53 |   display: none !important;
  54 | }
  55 | 
  56 | .visible-tablet {
  57 |   display: none !important;
  58 | }
  59 | 
  60 | .hidden-desktop {
  61 |   display: none !important;
  62 | }
  63 | 
  64 | .visible-desktop {
  65 |   display: inherit !important;
  66 | }
  67 | 
  68 | @media (min-width: 768px) and (max-width: 979px) {
  69 |   .hidden-desktop {
  70 |     display: inherit !important;
  71 |   }
  72 |   .visible-desktop {
  73 |     display: none !important ;
  74 |   }
  75 |   .visible-tablet {
  76 |     display: inherit !important;
  77 |   }
  78 |   .hidden-tablet {
  79 |     display: none !important;
  80 |   }
  81 | }
  82 | 
  83 | @media (max-width: 767px) {
  84 |   .hidden-desktop {
  85 |     display: inherit !important;
  86 |   }
  87 |   .visible-desktop {
  88 |     display: none !important;
  89 |   }
  90 |   .visible-phone {
  91 |     display: inherit !important;
  92 |   }
  93 |   .hidden-phone {
  94 |     display: none !important;
  95 |   }
  96 | }
  97 | 
  98 | @media (min-width: 1200px) {
  99 |   .row {
 100 |     margin-left: -30px;
 101 |     *zoom: 1;
 102 |   }
 103 |   .row:before,
 104 |   .row:after {
 105 |     display: table;
 106 |     line-height: 0;
 107 |     content: "";
 108 |   }
 109 |   .row:after {
 110 |     clear: both;
 111 |   }
 112 |   [class*="span"] {
 113 |     float: left;
 114 |     min-height: 1px;
 115 |     margin-left: 30px;
 116 |   }
 117 |   .container,
 118 |   .navbar-static-top .container,
 119 |   .navbar-fixed-top .container,
 120 |   .navbar-fixed-bottom .container {
 121 |     width: 1170px;
 122 |   }
 123 |   .span12 {
 124 |     width: 1170px;
 125 |   }
 126 |   .span11 {
 127 |     width: 1070px;
 128 |   }
 129 |   .span10 {
 130 |     width: 970px;
 131 |   }
 132 |   .span9 {
 133 |     width: 870px;
 134 |   }
 135 |   .span8 {
 136 |     width: 770px;
 137 |   }
 138 |   .span7 {
 139 |     width: 670px;
 140 |   }
 141 |   .span6 {
 142 |     width: 570px;
 143 |   }
 144 |   .span5 {
 145 |     width: 470px;
 146 |   }
 147 |   .span4 {
 148 |     width: 370px;
 149 |   }
 150 |   .span3 {
 151 |     width: 270px;
 152 |   }
 153 |   .span2 {
 154 |     width: 170px;
 155 |   }
 156 |   .span1 {
 157 |     width: 70px;
 158 |   }
 159 |   .offset12 {
 160 |     margin-left: 1230px;
 161 |   }
 162 |   .offset11 {
 163 |     margin-left: 1130px;
 164 |   }
 165 |   .offset10 {
 166 |     margin-left: 1030px;
 167 |   }
 168 |   .offset9 {
 169 |     margin-left: 930px;
 170 |   }
 171 |   .offset8 {
 172 |     margin-left: 830px;
 173 |   }
 174 |   .offset7 {
 175 |     margin-left: 730px;
 176 |   }
 177 |   .offset6 {
 178 |     margin-left: 630px;
 179 |   }
 180 |   .offset5 {
 181 |     margin-left: 530px;
 182 |   }
 183 |   .offset4 {
 184 |     margin-left: 430px;
 185 |   }
 186 |   .offset3 {
 187 |     margin-left: 330px;
 188 |   }
 189 |   .offset2 {
 190 |     margin-left: 230px;
 191 |   }
 192 |   .offset1 {
 193 |     margin-left: 130px;
 194 |   }
 195 |   .row-fluid {
 196 |     width: 100%;
 197 |     *zoom: 1;
 198 |   }
 199 |   .row-fluid:before,
 200 |   .row-fluid:after {
 201 |     display: table;
 202 |     line-height: 0;
 203 |     content: "";
 204 |   }
 205 |   .row-fluid:after {
 206 |     clear: both;
 207 |   }
 208 |   .row-fluid [class*="span"] {
 209 |     display: block;
 210 |     float: left;
 211 |     width: 100%;
 212 |     min-height: 30px;
 213 |     margin-left: 2.564102564102564%;
 214 |     *margin-left: 2.5109110747408616%;
 215 |     -webkit-box-sizing: border-box;
 216 |        -moz-box-sizing: border-box;
 217 |             box-sizing: border-box;
 218 |   }
 219 |   .row-fluid [class*="span"]:first-child {
 220 |     margin-left: 0;
 221 |   }
 222 |   .row-fluid .controls-row [class*="span"] + [class*="span"] {
 223 |     margin-left: 2.564102564102564%;
 224 |   }
 225 |   .row-fluid .span12 {
 226 |     width: 100%;
 227 |     *width: 99.94680851063829%;
 228 |   }
 229 |   .row-fluid .span11 {
 230 |     width: 91.45299145299145%;
 231 |     *width: 91.39979996362975%;
 232 |   }
 233 |   .row-fluid .span10 {
 234 |     width: 82.90598290598291%;
 235 |     *width: 82.8527914166212%;
 236 |   }
 237 |   .row-fluid .span9 {
 238 |     width: 74.35897435897436%;
 239 |     *width: 74.30578286961266%;
 240 |   }
 241 |   .row-fluid .span8 {
 242 |     width: 65.81196581196582%;
 243 |     *width: 65.75877432260411%;
 244 |   }
 245 |   .row-fluid .span7 {
 246 |     width: 57.26495726495726%;
 247 |     *width: 57.21176577559556%;
 248 |   }
 249 |   .row-fluid .span6 {
 250 |     width: 48.717948717948715%;
 251 |     *width: 48.664757228587014%;
 252 |   }
 253 |   .row-fluid .span5 {
 254 |     width: 40.17094017094017%;
 255 |     *width: 40.11774868157847%;
 256 |   }
 257 |   .row-fluid .span4 {
 258 |     width: 31.623931623931625%;
 259 |     *width: 31.570740134569924%;
 260 |   }
 261 |   .row-fluid .span3 {
 262 |     width: 23.076923076923077%;
 263 |     *width: 23.023731587561375%;
 264 |   }
 265 |   .row-fluid .span2 {
 266 |     width: 14.52991452991453%;
 267 |     *width: 14.476723040552828%;
 268 |   }
 269 |   .row-fluid .span1 {
 270 |     width: 5.982905982905983%;
 271 |     *width: 5.929714493544281%;
 272 |   }
 273 |   .row-fluid .offset12 {
 274 |     margin-left: 105.12820512820512%;
 275 |     *margin-left: 105.02182214948171%;
 276 |   }
 277 |   .row-fluid .offset12:first-child {
 278 |     margin-left: 102.56410256410257%;
 279 |     *margin-left: 102.45771958537915%;
 280 |   }
 281 |   .row-fluid .offset11 {
 282 |     margin-left: 96.58119658119658%;
 283 |     *margin-left: 96.47481360247316%;
 284 |   }
 285 |   .row-fluid .offset11:first-child {
 286 |     margin-left: 94.01709401709402%;
 287 |     *margin-left: 93.91071103837061%;
 288 |   }
 289 |   .row-fluid .offset10 {
 290 |     margin-left: 88.03418803418803%;
 291 |     *margin-left: 87.92780505546462%;
 292 |   }
 293 |   .row-fluid .offset10:first-child {
 294 |     margin-left: 85.47008547008548%;
 295 |     *margin-left: 85.36370249136206%;
 296 |   }
 297 |   .row-fluid .offset9 {
 298 |     margin-left: 79.48717948717949%;
 299 |     *margin-left: 79.38079650845607%;
 300 |   }
 301 |   .row-fluid .offset9:first-child {
 302 |     margin-left: 76.92307692307693%;
 303 |     *margin-left: 76.81669394435352%;
 304 |   }
 305 |   .row-fluid .offset8 {
 306 |     margin-left: 70.94017094017094%;
 307 |     *margin-left: 70.83378796144753%;
 308 |   }
 309 |   .row-fluid .offset8:first-child {
 310 |     margin-left: 68.37606837606839%;
 311 |     *margin-left: 68.26968539734497%;
 312 |   }
 313 |   .row-fluid .offset7 {
 314 |     margin-left: 62.393162393162385%;
 315 |     *margin-left: 62.28677941443899%;
 316 |   }
 317 |   .row-fluid .offset7:first-child {
 318 |     margin-left: 59.82905982905982%;
 319 |     *margin-left: 59.72267685033642%;
 320 |   }
 321 |   .row-fluid .offset6 {
 322 |     margin-left: 53.84615384615384%;
 323 |     *margin-left: 53.739770867430444%;
 324 |   }
 325 |   .row-fluid .offset6:first-child {
 326 |     margin-left: 51.28205128205128%;
 327 |     *margin-left: 51.175668303327875%;
 328 |   }
 329 |   .row-fluid .offset5 {
 330 |     margin-left: 45.299145299145295%;
 331 |     *margin-left: 45.1927623204219%;
 332 |   }
 333 |   .row-fluid .offset5:first-child {
 334 |     margin-left: 42.73504273504273%;
 335 |     *margin-left: 42.62865975631933%;
 336 |   }
 337 |   .row-fluid .offset4 {
 338 |     margin-left: 36.75213675213675%;
 339 |     *margin-left: 36.645753773413354%;
 340 |   }
 341 |   .row-fluid .offset4:first-child {
 342 |     margin-left: 34.18803418803419%;
 343 |     *margin-left: 34.081651209310785%;
 344 |   }
 345 |   .row-fluid .offset3 {
 346 |     margin-left: 28.205128205128204%;
 347 |     *margin-left: 28.0987452264048%;
 348 |   }
 349 |   .row-fluid .offset3:first-child {
 350 |     margin-left: 25.641025641025642%;
 351 |     *margin-left: 25.53464266230224%;
 352 |   }
 353 |   .row-fluid .offset2 {
 354 |     margin-left: 19.65811965811966%;
 355 |     *margin-left: 19.551736679396257%;
 356 |   }
 357 |   .row-fluid .offset2:first-child {
 358 |     margin-left: 17.094017094017094%;
 359 |     *margin-left: 16.98763411529369%;
 360 |   }
 361 |   .row-fluid .offset1 {
 362 |     margin-left: 11.11111111111111%;
 363 |     *margin-left: 11.004728132387708%;
 364 |   }
 365 |   .row-fluid .offset1:first-child {
 366 |     margin-left: 8.547008547008547%;
 367 |     *margin-left: 8.440625568285142%;
 368 |   }
 369 |   input,
 370 |   textarea,
 371 |   .uneditable-input {
 372 |     margin-left: 0;
 373 |   }
 374 |   .controls-row [class*="span"] + [class*="span"] {
 375 |     margin-left: 30px;
 376 |   }
 377 |   input.span12,
 378 |   textarea.span12,
 379 |   .uneditable-input.span12 {
 380 |     width: 1156px;
 381 |   }
 382 |   input.span11,
 383 |   textarea.span11,
 384 |   .uneditable-input.span11 {
 385 |     width: 1056px;
 386 |   }
 387 |   input.span10,
 388 |   textarea.span10,
 389 |   .uneditable-input.span10 {
 390 |     width: 956px;
 391 |   }
 392 |   input.span9,
 393 |   textarea.span9,
 394 |   .uneditable-input.span9 {
 395 |     width: 856px;
 396 |   }
 397 |   input.span8,
 398 |   textarea.span8,
 399 |   .uneditable-input.span8 {
 400 |     width: 756px;
 401 |   }
 402 |   input.span7,
 403 |   textarea.span7,
 404 |   .uneditable-input.span7 {
 405 |     width: 656px;
 406 |   }
 407 |   input.span6,
 408 |   textarea.span6,
 409 |   .uneditable-input.span6 {
 410 |     width: 556px;
 411 |   }
 412 |   input.span5,
 413 |   textarea.span5,
 414 |   .uneditable-input.span5 {
 415 |     width: 456px;
 416 |   }
 417 |   input.span4,
 418 |   textarea.span4,
 419 |   .uneditable-input.span4 {
 420 |     width: 356px;
 421 |   }
 422 |   input.span3,
 423 |   textarea.span3,
 424 |   .uneditable-input.span3 {
 425 |     width: 256px;
 426 |   }
 427 |   input.span2,
 428 |   textarea.span2,
 429 |   .uneditable-input.span2 {
 430 |     width: 156px;
 431 |   }
 432 |   input.span1,
 433 |   textarea.span1,
 434 |   .uneditable-input.span1 {
 435 |     width: 56px;
 436 |   }
 437 |   .thumbnails {
 438 |     margin-left: -30px;
 439 |   }
 440 |   .thumbnails > li {
 441 |     margin-left: 30px;
 442 |   }
 443 |   .row-fluid .thumbnails {
 444 |     margin-left: 0;
 445 |   }
 446 | }
 447 | 
 448 | @media (min-width: 768px) and (max-width: 979px) {
 449 |   .row {
 450 |     margin-left: -20px;
 451 |     *zoom: 1;
 452 |   }
 453 |   .row:before,
 454 |   .row:after {
 455 |     display: table;
 456 |     line-height: 0;
 457 |     content: "";
 458 |   }
 459 |   .row:after {
 460 |     clear: both;
 461 |   }
 462 |   [class*="span"] {
 463 |     float: left;
 464 |     min-height: 1px;
 465 |     margin-left: 20px;
 466 |   }
 467 |   .container,
 468 |   .navbar-static-top .container,
 469 |   .navbar-fixed-top .container,
 470 |   .navbar-fixed-bottom .container {
 471 |     width: 724px;
 472 |   }
 473 |   .span12 {
 474 |     width: 724px;
 475 |   }
 476 |   .span11 {
 477 |     width: 662px;
 478 |   }
 479 |   .span10 {
 480 |     width: 600px;
 481 |   }
 482 |   .span9 {
 483 |     width: 538px;
 484 |   }
 485 |   .span8 {
 486 |     width: 476px;
 487 |   }
 488 |   .span7 {
 489 |     width: 414px;
 490 |   }
 491 |   .span6 {
 492 |     width: 352px;
 493 |   }
 494 |   .span5 {
 495 |     width: 290px;
 496 |   }
 497 |   .span4 {
 498 |     width: 228px;
 499 |   }
 500 |   .span3 {
 501 |     width: 166px;
 502 |   }
 503 |   .span2 {
 504 |     width: 104px;
 505 |   }
 506 |   .span1 {
 507 |     width: 42px;
 508 |   }
 509 |   .offset12 {
 510 |     margin-left: 764px;
 511 |   }
 512 |   .offset11 {
 513 |     margin-left: 702px;
 514 |   }
 515 |   .offset10 {
 516 |     margin-left: 640px;
 517 |   }
 518 |   .offset9 {
 519 |     margin-left: 578px;
 520 |   }
 521 |   .offset8 {
 522 |     margin-left: 516px;
 523 |   }
 524 |   .offset7 {
 525 |     margin-left: 454px;
 526 |   }
 527 |   .offset6 {
 528 |     margin-left: 392px;
 529 |   }
 530 |   .offset5 {
 531 |     margin-left: 330px;
 532 |   }
 533 |   .offset4 {
 534 |     margin-left: 268px;
 535 |   }
 536 |   .offset3 {
 537 |     margin-left: 206px;
 538 |   }
 539 |   .offset2 {
 540 |     margin-left: 144px;
 541 |   }
 542 |   .offset1 {
 543 |     margin-left: 82px;
 544 |   }
 545 |   .row-fluid {
 546 |     width: 100%;
 547 |     *zoom: 1;
 548 |   }
 549 |   .row-fluid:before,
 550 |   .row-fluid:after {
 551 |     display: table;
 552 |     line-height: 0;
 553 |     content: "";
 554 |   }
 555 |   .row-fluid:after {
 556 |     clear: both;
 557 |   }
 558 |   .row-fluid [class*="span"] {
 559 |     display: block;
 560 |     float: left;
 561 |     width: 100%;
 562 |     min-height: 30px;
 563 |     margin-left: 2.7624309392265194%;
 564 |     *margin-left: 2.709239449864817%;
 565 |     -webkit-box-sizing: border-box;
 566 |        -moz-box-sizing: border-box;
 567 |             box-sizing: border-box;
 568 |   }
 569 |   .row-fluid [class*="span"]:first-child {
 570 |     margin-left: 0;
 571 |   }
 572 |   .row-fluid .controls-row [class*="span"] + [class*="span"] {
 573 |     margin-left: 2.7624309392265194%;
 574 |   }
 575 |   .row-fluid .span12 {
 576 |     width: 100%;
 577 |     *width: 99.94680851063829%;
 578 |   }
 579 |   .row-fluid .span11 {
 580 |     width: 91.43646408839778%;
 581 |     *width: 91.38327259903608%;
 582 |   }
 583 |   .row-fluid .span10 {
 584 |     width: 82.87292817679558%;
 585 |     *width: 82.81973668743387%;
 586 |   }
 587 |   .row-fluid .span9 {
 588 |     width: 74.30939226519337%;
 589 |     *width: 74.25620077583166%;
 590 |   }
 591 |   .row-fluid .span8 {
 592 |     width: 65.74585635359117%;
 593 |     *width: 65.69266486422946%;
 594 |   }
 595 |   .row-fluid .span7 {
 596 |     width: 57.18232044198895%;
 597 |     *width: 57.12912895262725%;
 598 |   }
 599 |   .row-fluid .span6 {
 600 |     width: 48.61878453038674%;
 601 |     *width: 48.56559304102504%;
 602 |   }
 603 |   .row-fluid .span5 {
 604 |     width: 40.05524861878453%;
 605 |     *width: 40.00205712942283%;
 606 |   }
 607 |   .row-fluid .span4 {
 608 |     width: 31.491712707182323%;
 609 |     *width: 31.43852121782062%;
 610 |   }
 611 |   .row-fluid .span3 {
 612 |     width: 22.92817679558011%;
 613 |     *width: 22.87498530621841%;
 614 |   }
 615 |   .row-fluid .span2 {
 616 |     width: 14.3646408839779%;
 617 |     *width: 14.311449394616199%;
 618 |   }
 619 |   .row-fluid .span1 {
 620 |     width: 5.801104972375691%;
 621 |     *width: 5.747913483013988%;
 622 |   }
 623 |   .row-fluid .offset12 {
 624 |     margin-left: 105.52486187845304%;
 625 |     *margin-left: 105.41847889972962%;
 626 |   }
 627 |   .row-fluid .offset12:first-child {
 628 |     margin-left: 102.76243093922652%;
 629 |     *margin-left: 102.6560479605031%;
 630 |   }
 631 |   .row-fluid .offset11 {
 632 |     margin-left: 96.96132596685082%;
 633 |     *margin-left: 96.8549429881274%;
 634 |   }
 635 |   .row-fluid .offset11:first-child {
 636 |     margin-left: 94.1988950276243%;
 637 |     *margin-left: 94.09251204890089%;
 638 |   }
 639 |   .row-fluid .offset10 {
 640 |     margin-left: 88.39779005524862%;
 641 |     *margin-left: 88.2914070765252%;
 642 |   }
 643 |   .row-fluid .offset10:first-child {
 644 |     margin-left: 85.6353591160221%;
 645 |     *margin-left: 85.52897613729868%;
 646 |   }
 647 |   .row-fluid .offset9 {
 648 |     margin-left: 79.8342541436464%;
 649 |     *margin-left: 79.72787116492299%;
 650 |   }
 651 |   .row-fluid .offset9:first-child {
 652 |     margin-left: 77.07182320441989%;
 653 |     *margin-left: 76.96544022569647%;
 654 |   }
 655 |   .row-fluid .offset8 {
 656 |     margin-left: 71.2707182320442%;
 657 |     *margin-left: 71.16433525332079%;
 658 |   }
 659 |   .row-fluid .offset8:first-child {
 660 |     margin-left: 68.50828729281768%;
 661 |     *margin-left: 68.40190431409427%;
 662 |   }
 663 |   .row-fluid .offset7 {
 664 |     margin-left: 62.70718232044199%;
 665 |     *margin-left: 62.600799341718584%;
 666 |   }
 667 |   .row-fluid .offset7:first-child {
 668 |     margin-left: 59.94475138121547%;
 669 |     *margin-left: 59.838368402492065%;
 670 |   }
 671 |   .row-fluid .offset6 {
 672 |     margin-left: 54.14364640883978%;
 673 |     *margin-left: 54.037263430116376%;
 674 |   }
 675 |   .row-fluid .offset6:first-child {
 676 |     margin-left: 51.38121546961326%;
 677 |     *margin-left: 51.27483249088986%;
 678 |   }
 679 |   .row-fluid .offset5 {
 680 |     margin-left: 45.58011049723757%;
 681 |     *margin-left: 45.47372751851417%;
 682 |   }
 683 |   .row-fluid .offset5:first-child {
 684 |     margin-left: 42.81767955801105%;
 685 |     *margin-left: 42.71129657928765%;
 686 |   }
 687 |   .row-fluid .offset4 {
 688 |     margin-left: 37.01657458563536%;
 689 |     *margin-left: 36.91019160691196%;
 690 |   }
 691 |   .row-fluid .offset4:first-child {
 692 |     margin-left: 34.25414364640884%;
 693 |     *margin-left: 34.14776066768544%;
 694 |   }
 695 |   .row-fluid .offset3 {
 696 |     margin-left: 28.45303867403315%;
 697 |     *margin-left: 28.346655695309746%;
 698 |   }
 699 |   .row-fluid .offset3:first-child {
 700 |     margin-left: 25.69060773480663%;
 701 |     *margin-left: 25.584224756083227%;
 702 |   }
 703 |   .row-fluid .offset2 {
 704 |     margin-left: 19.88950276243094%;
 705 |     *margin-left: 19.783119783707537%;
 706 |   }
 707 |   .row-fluid .offset2:first-child {
 708 |     margin-left: 17.12707182320442%;
 709 |     *margin-left: 17.02068884448102%;
 710 |   }
 711 |   .row-fluid .offset1 {
 712 |     margin-left: 11.32596685082873%;
 713 |     *margin-left: 11.219583872105325%;
 714 |   }
 715 |   .row-fluid .offset1:first-child {
 716 |     margin-left: 8.56353591160221%;
 717 |     *margin-left: 8.457152932878806%;
 718 |   }
 719 |   input,
 720 |   textarea,
 721 |   .uneditable-input {
 722 |     margin-left: 0;
 723 |   }
 724 |   .controls-row [class*="span"] + [class*="span"] {
 725 |     margin-left: 20px;
 726 |   }
 727 |   input.span12,
 728 |   textarea.span12,
 729 |   .uneditable-input.span12 {
 730 |     width: 710px;
 731 |   }
 732 |   input.span11,
 733 |   textarea.span11,
 734 |   .uneditable-input.span11 {
 735 |     width: 648px;
 736 |   }
 737 |   input.span10,
 738 |   textarea.span10,
 739 |   .uneditable-input.span10 {
 740 |     width: 586px;
 741 |   }
 742 |   input.span9,
 743 |   textarea.span9,
 744 |   .uneditable-input.span9 {
 745 |     width: 524px;
 746 |   }
 747 |   input.span8,
 748 |   textarea.span8,
 749 |   .uneditable-input.span8 {
 750 |     width: 462px;
 751 |   }
 752 |   input.span7,
 753 |   textarea.span7,
 754 |   .uneditable-input.span7 {
 755 |     width: 400px;
 756 |   }
 757 |   input.span6,
 758 |   textarea.span6,
 759 |   .uneditable-input.span6 {
 760 |     width: 338px;
 761 |   }
 762 |   input.span5,
 763 |   textarea.span5,
 764 |   .uneditable-input.span5 {
 765 |     width: 276px;
 766 |   }
 767 |   input.span4,
 768 |   textarea.span4,
 769 |   .uneditable-input.span4 {
 770 |     width: 214px;
 771 |   }
 772 |   input.span3,
 773 |   textarea.span3,
 774 |   .uneditable-input.span3 {
 775 |     width: 152px;
 776 |   }
 777 |   input.span2,
 778 |   textarea.span2,
 779 |   .uneditable-input.span2 {
 780 |     width: 90px;
 781 |   }
 782 |   input.span1,
 783 |   textarea.span1,
 784 |   .uneditable-input.span1 {
 785 |     width: 28px;
 786 |   }
 787 | }
 788 | 
 789 | @media (max-width: 767px) {
 790 |   body {
 791 |     padding-right: 20px;
 792 |     padding-left: 20px;
 793 |   }
 794 |   .navbar-fixed-top,
 795 |   .navbar-fixed-bottom,
 796 |   .navbar-static-top {
 797 |     margin-right: -20px;
 798 |     margin-left: -20px;
 799 |   }
 800 |   .container-fluid {
 801 |     padding: 0;
 802 |   }
 803 |   .dl-horizontal dt {
 804 |     float: none;
 805 |     width: auto;
 806 |     clear: none;
 807 |     text-align: left;
 808 |   }
 809 |   .dl-horizontal dd {
 810 |     margin-left: 0;
 811 |   }
 812 |   .container {
 813 |     width: auto;
 814 |   }
 815 |   .row-fluid {
 816 |     width: 100%;
 817 |   }
 818 |   .row,
 819 |   .thumbnails {
 820 |     margin-left: 0;
 821 |   }
 822 |   .thumbnails > li {
 823 |     float: none;
 824 |     margin-left: 0;
 825 |   }
 826 |   [class*="span"],
 827 |   .uneditable-input[class*="span"],
 828 |   .row-fluid [class*="span"] {
 829 |     display: block;
 830 |     float: none;
 831 |     width: 100%;
 832 |     margin-left: 0;
 833 |     -webkit-box-sizing: border-box;
 834 |        -moz-box-sizing: border-box;
 835 |             box-sizing: border-box;
 836 |   }
 837 |   .span12,
 838 |   .row-fluid .span12 {
 839 |     width: 100%;
 840 |     -webkit-box-sizing: border-box;
 841 |        -moz-box-sizing: border-box;
 842 |             box-sizing: border-box;
 843 |   }
 844 |   .row-fluid [class*="offset"]:first-child {
 845 |     margin-left: 0;
 846 |   }
 847 |   .input-large,
 848 |   .input-xlarge,
 849 |   .input-xxlarge,
 850 |   input[class*="span"],
 851 |   select[class*="span"],
 852 |   textarea[class*="span"],
 853 |   .uneditable-input {
 854 |     display: block;
 855 |     width: 100%;
 856 |     min-height: 30px;
 857 |     -webkit-box-sizing: border-box;
 858 |        -moz-box-sizing: border-box;
 859 |             box-sizing: border-box;
 860 |   }
 861 |   .input-prepend input,
 862 |   .input-append input,
 863 |   .input-prepend input[class*="span"],
 864 |   .input-append input[class*="span"] {
 865 |     display: inline-block;
 866 |     width: auto;
 867 |   }
 868 |   .controls-row [class*="span"] + [class*="span"] {
 869 |     margin-left: 0;
 870 |   }
 871 |   .modal {
 872 |     position: fixed;
 873 |     top: 20px;
 874 |     right: 20px;
 875 |     left: 20px;
 876 |     width: auto;
 877 |     margin: 0;
 878 |   }
 879 |   .modal.fade {
 880 |     top: -100px;
 881 |   }
 882 |   .modal.fade.in {
 883 |     top: 20px;
 884 |   }
 885 | }
 886 | 
 887 | @media (max-width: 480px) {
 888 |   .nav-collapse {
 889 |     -webkit-transform: translate3d(0, 0, 0);
 890 |   }
 891 |   .page-header h1 small {
 892 |     display: block;
 893 |     line-height: 20px;
 894 |   }
 895 |   input[type="checkbox"],
 896 |   input[type="radio"] {
 897 |     border: 1px solid #ccc;
 898 |   }
 899 |   .form-horizontal .control-label {
 900 |     float: none;
 901 |     width: auto;
 902 |     padding-top: 0;
 903 |     text-align: left;
 904 |   }
 905 |   .form-horizontal .controls {
 906 |     margin-left: 0;
 907 |   }
 908 |   .form-horizontal .control-list {
 909 |     padding-top: 0;
 910 |   }
 911 |   .form-horizontal .form-actions {
 912 |     padding-right: 10px;
 913 |     padding-left: 10px;
 914 |   }
 915 |   .media .pull-left,
 916 |   .media .pull-right {
 917 |     display: block;
 918 |     float: none;
 919 |     margin-bottom: 10px;
 920 |   }
 921 |   .media-object {
 922 |     margin-right: 0;
 923 |     margin-left: 0;
 924 |   }
 925 |   .modal {
 926 |     top: 10px;
 927 |     right: 10px;
 928 |     left: 10px;
 929 |   }
 930 |   .modal-header .close {
 931 |     padding: 10px;
 932 |     margin: -10px;
 933 |   }
 934 |   .carousel-caption {
 935 |     position: static;
 936 |   }
 937 | }
 938 | 
 939 | @media (max-width: 979px) {
 940 |   body {
 941 |     padding-top: 0;
 942 |   }
 943 |   .navbar-fixed-top,
 944 |   .navbar-fixed-bottom {
 945 |     position: static;
 946 |   }
 947 |   .navbar-fixed-top {
 948 |     margin-bottom: 20px;
 949 |   }
 950 |   .navbar-fixed-bottom {
 951 |     margin-top: 20px;
 952 |   }
 953 |   .navbar-fixed-top .navbar-inner,
 954 |   .navbar-fixed-bottom .navbar-inner {
 955 |     padding: 5px;
 956 |   }
 957 |   .navbar .container {
 958 |     width: auto;
 959 |     padding: 0;
 960 |   }
 961 |   .navbar .brand {
 962 |     padding-right: 10px;
 963 |     padding-left: 10px;
 964 |     margin: 0 0 0 -5px;
 965 |   }
 966 |   .nav-collapse {
 967 |     clear: both;
 968 |   }
 969 |   .nav-collapse .nav {
 970 |     float: none;
 971 |     margin: 0 0 10px;
 972 |   }
 973 |   .nav-collapse .nav > li {
 974 |     float: none;
 975 |   }
 976 |   .nav-collapse .nav > li > a {
 977 |     margin-bottom: 2px;
 978 |   }
 979 |   .nav-collapse .nav > .divider-vertical {
 980 |     display: none;
 981 |   }
 982 |   .nav-collapse .nav .nav-header {
 983 |     color: #777777;
 984 |     text-shadow: none;
 985 |   }
 986 |   .nav-collapse .nav > li > a,
 987 |   .nav-collapse .dropdown-menu a {
 988 |     padding: 9px 15px;
 989 |     font-weight: bold;
 990 |     color: #777777;
 991 |     -webkit-border-radius: 3px;
 992 |        -moz-border-radius: 3px;
 993 |             border-radius: 3px;
 994 |   }
 995 |   .nav-collapse .btn {
 996 |     padding: 4px 10px 4px;
 997 |     font-weight: normal;
 998 |     -webkit-border-radius: 4px;
 999 |        -moz-border-radius: 4px;
1000 |             border-radius: 4px;
1001 |   }
1002 |   .nav-collapse .dropdown-menu li + li a {
1003 |     margin-bottom: 2px;
1004 |   }
1005 |   .nav-collapse .nav > li > a:hover,
1006 |   .nav-collapse .dropdown-menu a:hover {
1007 |     background-color: #f2f2f2;
1008 |   }
1009 |   .navbar-inverse .nav-collapse .nav > li > a,
1010 |   .navbar-inverse .nav-collapse .dropdown-menu a {
1011 |     color: #999999;
1012 |   }
1013 |   .navbar-inverse .nav-collapse .nav > li > a:hover,
1014 |   .navbar-inverse .nav-collapse .dropdown-menu a:hover {
1015 |     background-color: #111111;
1016 |   }
1017 |   .nav-collapse.in .btn-group {
1018 |     padding: 0;
1019 |     margin-top: 5px;
1020 |   }
1021 |   .nav-collapse .dropdown-menu {
1022 |     position: static;
1023 |     top: auto;
1024 |     left: auto;
1025 |     display: none;
1026 |     float: none;
1027 |     max-width: none;
1028 |     padding: 0;
1029 |     margin: 0 15px;
1030 |     background-color: transparent;
1031 |     border: none;
1032 |     -webkit-border-radius: 0;
1033 |        -moz-border-radius: 0;
1034 |             border-radius: 0;
1035 |     -webkit-box-shadow: none;
1036 |        -moz-box-shadow: none;
1037 |             box-shadow: none;
1038 |   }
1039 |   .nav-collapse .open > .dropdown-menu {
1040 |     display: block;
1041 |   }
1042 |   .nav-collapse .dropdown-menu:before,
1043 |   .nav-collapse .dropdown-menu:after {
1044 |     display: none;
1045 |   }
1046 |   .nav-collapse .dropdown-menu .divider {
1047 |     display: none;
1048 |   }
1049 |   .nav-collapse .nav > li > .dropdown-menu:before,
1050 |   .nav-collapse .nav > li > .dropdown-menu:after {
1051 |     display: none;
1052 |   }
1053 |   .nav-collapse .navbar-form,
1054 |   .nav-collapse .navbar-search {
1055 |     float: none;
1056 |     padding: 10px 15px;
1057 |     margin: 10px 0;
1058 |     border-top: 1px solid #f2f2f2;
1059 |     border-bottom: 1px solid #f2f2f2;
1060 |     -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1061 |        -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1062 |             box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1063 |   }
1064 |   .navbar-inverse .nav-collapse .navbar-form,
1065 |   .navbar-inverse .nav-collapse .navbar-search {
1066 |     border-top-color: #111111;
1067 |     border-bottom-color: #111111;
1068 |   }
1069 |   .navbar .nav-collapse .nav.pull-right {
1070 |     float: none;
1071 |     margin-left: 0;
1072 |   }
1073 |   .nav-collapse,
1074 |   .nav-collapse.collapse {
1075 |     height: 0;
1076 |     overflow: hidden;
1077 |   }
1078 |   .navbar .btn-navbar {
1079 |     display: block;
1080 |   }
1081 |   .navbar-static .navbar-inner {
1082 |     padding-right: 10px;
1083 |     padding-left: 10px;
1084 |   }
1085 | }
1086 | 
1087 | @media (min-width: 980px) {
1088 |   .nav-collapse.collapse {
1089 |     height: auto !important;
1090 |     overflow: visible !important;
1091 |   }
1092 | }
1093 | 
1094 | 


--------------------------------------------------------------------------------
/assets/ico/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdewit/bootstrap-timepicker/f8f52c45986b6cc5ab54b135839441e8da47ab6d/assets/ico/favicon.ico


--------------------------------------------------------------------------------
/assets/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdewit/bootstrap-timepicker/f8f52c45986b6cc5ab54b135839441e8da47ab6d/assets/images/bg.png


--------------------------------------------------------------------------------
/assets/prettify/prettify.css:
--------------------------------------------------------------------------------
 1 | .com {
 2 |     color: #93A1A1;
 3 | }
 4 | .lit {
 5 |     color: #195F91;
 6 | }
 7 | .pun, .opn, .clo {
 8 |     color: #93A1A1;
 9 | }
10 | .fun {
11 |     color: #DC322F;
12 | }
13 | .str, .atv {
14 |     color: #DD1144;
15 | }
16 | .kwd, .linenums .tag {
17 |     color: #1E347B;
18 | }
19 | .typ, .atn, .dec, .var {
20 |     color: teal;
21 | }
22 | .pln {
23 |     color: #48484C;
24 | }
25 | .prettyprint {
26 |     background-color: #F7F7F9;
27 |     border: 1px solid #E1E1E8;
28 |     padding: 8px;
29 | }
30 | .prettyprint.linenums {
31 |     box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset;
32 | }
33 | ol.linenums {
34 |     margin: 0 0 0 33px;
35 | }
36 | ol.linenums li {
37 |     color: #BEBEC5;
38 |     line-height: 18px;
39 |     padding-left: 12px;
40 |     text-shadow: 0 1px 0 #FFFFFF;
41 | }
42 | 
43 | 


--------------------------------------------------------------------------------
/assets/prettify/prettify.js:
--------------------------------------------------------------------------------
 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
 4 | f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
 5 | (j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
 6 | {b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
 7 | t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
 8 | "string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
 9 | l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-
#39;./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
15 | m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
16 | a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
17 | j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
27 | 250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
28 | PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
29 | 
30 | 


--------------------------------------------------------------------------------
/assets/pygments.css:
--------------------------------------------------------------------------------
 1 | .hll { background-color: #ffffcc }
 2 | .c { color: #408080; font-style: italic } /* Comment */
 3 | .k { color: #008000; font-weight: bold } /* Keyword */
 4 | .o { color: #666666 } /* Operator */
 5 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */
 6 | .cp { color: #BC7A00 } /* Comment.Preproc */
 7 | .c1 { color: #408080; font-style: italic } /* Comment.Single */
 8 | .cs { color: #408080; font-style: italic } /* Comment.Special */
 9 | .gd { color: #A00000 } /* Generic.Deleted */
10 | .ge { font-style: italic } /* Generic.Emph */
11 | .gr { color: #FF0000 } /* Generic.Error */
12 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */
13 | .gi { color: #00A000 } /* Generic.Inserted */
14 | .go { color: #808080 } /* Generic.Output */
15 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
16 | .gs { font-weight: bold } /* Generic.Strong */
17 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
18 | .gt { color: #0040D0 } /* Generic.Traceback */
19 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
20 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
21 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
22 | .kp { color: #008000 } /* Keyword.Pseudo */
23 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
24 | .kt { color: #B00040 } /* Keyword.Type */
25 | .m { color: #666666 } /* Literal.Number */
26 | .s { color: #BA2121 } /* Literal.String */
27 | .na { color: #7D9029 } /* Name.Attribute */
28 | .nb { color: #008000 } /* Name.Builtin */
29 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */
30 | .no { color: #880000 } /* Name.Constant */
31 | .nd { color: #AA22FF } /* Name.Decorator */
32 | .ni { color: #999999; font-weight: bold } /* Name.Entity */
33 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
34 | .nf { color: #0000FF } /* Name.Function */
35 | .nl { color: #A0A000 } /* Name.Label */
36 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
37 | .nt { color: #008000; font-weight: bold } /* Name.Tag */
38 | .nv { color: #19177C } /* Name.Variable */
39 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
40 | .w { color: #bbbbbb } /* Text.Whitespace */
41 | .mf { color: #666666 } /* Literal.Number.Float */
42 | .mh { color: #666666 } /* Literal.Number.Hex */
43 | .mi { color: #666666 } /* Literal.Number.Integer */
44 | .mo { color: #666666 } /* Literal.Number.Oct */
45 | .sb { color: #BA2121 } /* Literal.String.Backtick */
46 | .sc { color: #BA2121 } /* Literal.String.Char */
47 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
48 | .s2 { color: #BA2121 } /* Literal.String.Double */
49 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
50 | .sh { color: #BA2121 } /* Literal.String.Heredoc */
51 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
52 | .sx { color: #008000 } /* Literal.String.Other */
53 | .sr { color: #BB6688 } /* Literal.String.Regex */
54 | .s1 { color: #BA2121 } /* Literal.String.Single */
55 | .ss { color: #19177C } /* Literal.String.Symbol */
56 | .bp { color: #008000 } /* Name.Builtin.Pseudo */
57 | .vc { color: #19177C } /* Name.Variable.Class */
58 | .vg { color: #19177C } /* Name.Variable.Global */
59 | .vi { color: #19177C } /* Name.Variable.Instance */
60 | .il { color: #666666 } /* Literal.Number.Integer.Long */
61 | 


--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "name": "bootstrap-timepicker",
 3 |     "description": "A timepicker component for Twitter Bootstrap",
 4 |     "version": "0.5.2",
 5 |     "main": "js/bootstrap-timepicker.js",
 6 |     "license": "MIT",
 7 |     "ignore": [
 8 |         "**/.*",
 9 |         "_layouts",
10 |         "node_modules",
11 |         "_config.yml",
12 |         "assets",
13 |         "spec",
14 |         "index.html",
15 |         "Gruntfile.js",
16 |         "package.json",
17 |         "composer.json"
18 |     ],
19 |     "repository": {
20 |       "type": "git",
21 |       "url": "https://github.com/jdewit/bootstrap-timepicker"
22 |     },
23 |     "dependencies": {
24 |         "bootstrap": "^3.0",
25 |         "jquery": "^2.0"
26 |     },
27 |     "devDependencies": {
28 |         "autotype": "https://raw.github.com/mmonteleone/jquery.autotype/master/jquery.autotype.js"
29 |     },
30 |     "keywords": [
31 |         "widget",
32 |         "timepicker",
33 |         "time"
34 |     ]
35 | }
36 | 


--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
 1 | {
 2 | 	"name"        : "jdewit/bootstrap-timepicker",
 3 | 	"description" : "A simple timepicker component for Twitter Bootstrap.",
 4 | 	"version"     : "0.5.2",
 5 | 	"license"     : "MIT",
 6 | 	"authors": [
 7 | 		{
 8 | 	    "name" : "Joris de Wit",
 9 | 	    "email" : "joris.w.dewit@gmail.com"
10 | 		}
11 | 	]
12 | 
13 | }
14 | 


--------------------------------------------------------------------------------
/css/timepicker.less:
--------------------------------------------------------------------------------
  1 | /*!
  2 |  * Timepicker Component for Twitter Bootstrap
  3 |  *
  4 |  * Copyright 2013 Joris de Wit
  5 |  *
  6 |  * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
  7 |  *
  8 |  * For the full copyright and license information, please view the LICENSE
  9 |  * file that was distributed with this source code.
 10 |  */
 11 | .bootstrap-timepicker {
 12 |     position: relative;
 13 | 
 14 |     &.pull-right {
 15 |         .bootstrap-timepicker-widget {
 16 |             &.dropdown-menu {
 17 |                 left: auto;
 18 |                 right: 0;
 19 | 
 20 |                 &:before {
 21 |                     left: auto;
 22 |                     right: 12px;
 23 |                 }
 24 |                 &:after {
 25 |                     left: auto;
 26 |                     right: 13px;
 27 |                 }
 28 |             }
 29 |         }
 30 |     }
 31 | 
 32 |     .input-group-addon {
 33 |         cursor: pointer;
 34 |         i {
 35 |            display: inline-block;
 36 |            width: 16px;
 37 |            height: 16px;
 38 |         }
 39 |     }
 40 | }
 41 | .bootstrap-timepicker-widget {
 42 |     &.dropdown-menu {
 43 |         padding: 4px;
 44 |         &.open {
 45 |             display: inline-block;
 46 |         }
 47 |         &:before {
 48 |             border-bottom: 7px solid rgba(0, 0, 0, 0.2);
 49 |             border-left: 7px solid transparent;
 50 |             border-right: 7px solid transparent;
 51 |             content: "";
 52 |             display: inline-block;
 53 |             position: absolute;
 54 |         }
 55 |         &:after {
 56 |             border-bottom: 6px solid #FFFFFF;
 57 |             border-left: 6px solid transparent;
 58 |             border-right: 6px solid transparent;
 59 |             content: "";
 60 |             display: inline-block;
 61 |             position: absolute;
 62 |         }
 63 |     }
 64 |     &.timepicker-orient-left {
 65 |         &:before {
 66 |            left: 6px;
 67 |         }
 68 |         &:after {
 69 |            left: 7px;
 70 |         }
 71 |     }
 72 |     &.timepicker-orient-right {
 73 |         &:before {
 74 |            right: 6px;
 75 |         }
 76 |         &:after {
 77 |            right: 7px;
 78 |         }
 79 |     }
 80 |     &.timepicker-orient-top {
 81 |         &:before {
 82 |            top: -7px;
 83 |         }
 84 |         &:after {
 85 |             top: -6px;
 86 |         }
 87 |     }
 88 |     &.timepicker-orient-bottom {
 89 |         &:before {
 90 |             bottom: -7px;
 91 |             border-bottom: 0;
 92 |             border-top: 7px solid #999;
 93 |         }
 94 |         &:after {
 95 |             bottom: -6px;
 96 |             border-bottom: 0;
 97 |             border-top: 6px solid #ffffff;
 98 |         }
 99 |     }
100 |     a.btn, input {
101 |         border-radius: 4px;
102 |     }
103 | 
104 |     table {
105 |         width: 100%;
106 |         margin: 0;
107 | 
108 |         td {
109 |             text-align: center;
110 |             height: 30px;
111 |             margin: 0;
112 |             padding: 2px;
113 | 
114 |             &:not(.separator) {
115 |                 min-width: 30px;
116 |             }
117 | 
118 |             span {
119 |                 width: 100%;
120 |             }
121 |             a {
122 |                 border: 1px transparent solid;
123 |                 width: 100%;
124 |                 display: inline-block;
125 |                 margin: 0;
126 |                 padding: 8px 0;
127 |                 outline: 0;
128 |                 color: #333;
129 | 
130 |                 &:hover {
131 |                     text-decoration: none;
132 |                     background-color: #eee;
133 |                     -webkit-border-radius: 4px;
134 |                     -moz-border-radius: 4px;
135 |                     border-radius: 4px;
136 |                     border-color: #ddd;
137 |                 }
138 | 
139 |                 i {
140 |                     margin-top: 2px;
141 |                     font-size: 18px;
142 |                 }
143 |             }
144 |             input {
145 |                 width: 25px;
146 |                 margin: 0;
147 |                 text-align: center;
148 |             }
149 |         }
150 |     }
151 | }
152 | 
153 | .bootstrap-timepicker-widget .modal-content {
154 |     padding: 4px;
155 | }
156 | 
157 | @media (min-width: 767px) {
158 |     .bootstrap-timepicker-widget.modal {
159 |         width: 200px;
160 |         margin-left: -100px;
161 |     }
162 | }
163 | 
164 | @media (max-width: 767px) {
165 |     .bootstrap-timepicker {
166 |         width: 100%;
167 | 
168 |         .dropdown-menu {
169 |             width: 100%;
170 |         }
171 |     }
172 | }
173 | 


--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
  1 | ---
  2 | layout: default
  3 | title: Timepicker for Twitter Bootstrap
  4 | ---
  5 | 
  6 | 
  7 | <!-- Timepicker
  8 | ================================================== -->
  9 | <section id="timepicker">
 10 |     <div class="center jumbotron" style="margin: 0; padding: 0;">
 11 |         <br>
 12 |         <br>
 13 |         <br>
 14 |         <h1>Bootstrap Timepicker</h1>
 15 |         <br>
 16 |         <p class="lead">Easily select a time for a text input using your mouse or keyboards arrow keys.</p>
 17 |         <p><a href="http://travis-ci.org/jdewit/bootstrap-timepicker"><img style="max-width:100%; height: 15px;" alt="Build Status" src="https://secure.travis-ci.org/jdewit/bootstrap-timepicker.png" target="_blank"></a></p>
 18 |         <hr>
 19 |         <div class="row">
 20 |             <div class="span4 center">
 21 |                 <a href="https://github.com/jdewit/bootstrap-timepicker" target="_blank" class="btn btn-large btn-primary"><i class="icon-share-alt icon-white"></i> View Github Project</a>
 22 |             </div>
 23 |             <div class="span4 center">
 24 |                 <a href="http://www.pledgie.com/campaigns/19125"><img alt="Click here to support bootstrap-timepicker and make a donation with pledgie!" src="http://www.pledgie.com/campaigns/19125.png?skin_name=chrome" border="0" target="_blank"/></a>
 25 |             </div>
 26 |             <div class="span4 center">
 27 | <a class="FlattrButton" style="display:none;" rev="flattr;button:compact;" href="http://jdewit.github.com/bootstrap-timepicker"></a>
 28 | <noscript><a href="http://flattr.com/thing/1116513/Bootstrap-Timepicker" target="_blank">
 29 | <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a></noscript>
 30 |             </div>
 31 |         </div>
 32 |     </div>
 33 |     <br>
 34 |     <br>
 35 |     <h1>Installation</h1>
 36 |     <hr>
 37 |     <div>
 38 |         <p>This project is registered as a <a href="http://bower.io">Bower</a> package, and can be insalled with the following command:</p>
 39 | <pre class="prettyprint linenums">
 40 |     $ bower install bootstrap-timepicker
 41 | </pre>
 42 |         <p>
 43 |             You can also download our latest release (and any previous release) 
 44 |             <a href="https://github.com/jdewit/bootstrap-timepicker/releases">here</a>.
 45 |         </p>
 46 |     </div>
 47 |     <hr>
 48 |     <h1>Demos</h1>
 49 |     <hr>
 50 |     <p>Default timepicker. Value = (<span id="timeDisplay"></span>)</p>
 51 |     <div class="input-group bootstrap-timepicker timepicker">
 52 |         <input id="timepicker1" class="form-control input-small" type="text"/><span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
 53 |     </div>
 54 |     &nbsp;
 55 |     <a href="#timepicker1Source" data-toggle="collapse">+ View Source</a>
 56 |     <div id="timepicker1Source" class="collapse">
 57 | <pre class="prettyprint linenums">
 58 | &lt;!DOCTYPE html&gt;
 59 | &lt;html&gt;
 60 |     &lt;head&gt;
 61 |         &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
 62 |         &lt;link type="text/css" href="css/bootstrap.min.css" /&gt;
 63 |         &lt;link type="text/css" href="css/bootstrap-timepicker.min.css" /&gt;
 64 |         &lt;script type="text/javascript" src="js/jquery.min.js"&gt;&lt;/script&gt;
 65 |         &lt;script type="text/javascript" src="js/bootstrap.min.js"&gt;&lt;/script&gt;
 66 |         &lt;script type="text/javascript" src="js/bootstrap-timepicker.min.js"&gt;&lt;/script&gt;
 67 |     &lt;/head&gt;
 68 |     &lt;body&gt;
 69 |         &lt;div class="input-group bootstrap-timepicker timepicker"&gt;
 70 |             &lt;input id="timepicker1" type="text" class="form-control input-small"&gt;
 71 |             &lt;span class="input-group-addon"&gt;&lt;i class="glyphicon glyphicon-time"&gt;&lt;/i&gt;&lt;/span&gt;
 72 |         &lt;/div&gt;
 73 | 
 74 |         &lt;script type="text/javascript"&gt;
 75 |             $('#timepicker1').timepicker();
 76 |         &lt;/script&gt;
 77 |     &lt;/body&gt;
 78 | &lt;/html&gt;
 79 | </pre>
 80 |     </div>
 81 |     <hr class="soften">
 82 |     <p>Inside a modal with 24hr mode and seconds enabled.</p>
 83 |     <div class="input-group bootstrap-timepicker timepicker">
 84 |         <input id="timepicker2" class="form-control input-small" type="text"/><span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
 85 |     </div>
 86 |     &nbsp;
 87 |     <a href="#timepicker2Source" data-toggle="collapse">+ View Source</a>
 88 |     <div id="timepicker2Source" class="collapse">
 89 | <pre class="prettyprint linenums">
 90 | &lt;!DOCTYPE html&gt;
 91 | &lt;html&gt;
 92 |     &lt;head&gt;
 93 |         &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
 94 |         &lt;link type="text/css" href="css/bootstrap.min.css" /&gt;
 95 |         &lt;link type="text/css" href="css/bootstrap-timepicker.min.css" /&gt;
 96 |         &lt;script type="text/javascript" src="js/jquery.min.js"&gt;&lt;/script&gt;
 97 |         &lt;script type="text/javascript" src="js/bootstrap.min.js"&gt;&lt;/script&gt;
 98 |         &lt;script type="text/javascript" src="js/bootstrap-timepicker.min.js"&gt;&lt;/script&gt;
 99 |     &lt;/head&gt;
100 |     &lt;body&gt;
101 |         &lt;div class="input-group bootstrap-timepicker timepicker"&gt;
102 |             &lt;input id="timepicker2" type="text" class="form-control input-small"&gt;
103 |             &lt;span class="input-group-addon"&gt;
104 |                 &lt;i class="glyphicon glyphicon-time"&gt;&lt;/i&gt;
105 |             &lt;/span&gt;
106 |         &lt;/div&gt;
107 | 
108 |         &lt;script type="text/javascript"&gt;
109 |             $('#timepicker2').timepicker({
110 |                 minuteStep: 1,
111 |                 template: 'modal',
112 |                 appendWidgetTo: 'body',
113 |                 showSeconds: true,
114 |                 showMeridian: false,
115 |                 defaultTime: false
116 |             });
117 |         &lt;/script&gt;
118 |     &lt;/body&gt;
119 | &lt;/html&gt;
120 | </pre>
121 |    </div>
122 |    <hr class="soften">
123 |    <p>Without component markup, keyboard input disabled and floated right.</p>
124 |    <div class="input-group bootstrap-timepicker timepicker pull-right" style="height: 2em;">
125 |         <a  href="#timepicker3Source" data-toggle="collapse">+ View Source </a>
126 |         &nbsp;
127 |        <input id="timepicker3" class="form-control input-small" type="text" />
128 |    </div>
129 |    <br>
130 |    <br>
131 |    <div id="timepicker3Source" class="collapse">
132 | <pre class="prettyprint linenums">
133 | &lt;!DOCTYPE html&gt;
134 | &lt;html&gt;
135 |     &lt;head&gt;
136 |         &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
137 |         &lt;link type="text/css" href="css/bootstrap.min.css" /&gt;
138 |         &lt;link type="text/css" href="css/bootstrap-timepicker.min.css" /&gt;
139 |         &lt;script type="text/javascript" src="js/jquery.min.js"&gt;&lt;/script&gt;
140 |         &lt;script type="text/javascript" src="js/bootstrap.min.js"&gt;&lt;/script&gt;
141 |         &lt;script type="text/javascript" src="js/bootstrap-timepicker.min.js"&gt;&lt;/script&gt;
142 |     &lt;/head&gt;
143 |     &lt;body&gt;
144 |         &lt;div class="input-group bootstrap-timepicker timepicker pull-right"&gt;
145 |             &lt;input id="timepicker3" type="text" class="form-control input-small"&gt;
146 |         &lt;/div&gt;
147 | 
148 |         &lt;script type="text/javascript"&gt;
149 |             $('#timepicker3').timepicker({
150 |                 minuteStep: 5,
151 |                 showInputs: false,
152 |                 disableFocus: true
153 |             });
154 |         &lt;/script&gt;
155 |     &lt;/body&gt;
156 | &lt;/html&gt;
157 | </pre>
158 |    </div>
159 |    <hr class="soften">
160 |    <p>Inside a modal with backdrop enabled, inputs disabled and with a preset value.</p>
161 | 		<div id="timepicker4Modal" class="modal hide fade">
162 | 			<div class="modal-header">
163 | 				<h1>A Timepicker Inside A Modal</h1>
164 | 			</div>
165 | 			<div class="modal-body" style="min-height: 150px;">
166 | 				 <span class="input-group bootstrap-timepicker timepicker" style="vertical-align: middle;">
167 | 						 <input id="timepicker4" class="form-control input-small" value="10:35 AM" type="text" />
168 | 						 <i class="glyphicon glyphicon-time" style="margin: -2px 0 0 -22.5px; pointer-events: none; position: relative;"></i>
169 | 				 </span>
170 | 			</div>
171 | 			<div class="modal-footer">
172 | 				<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>
173 | 			</div>
174 | 		</div>
175 |    &nbsp;
176 |    &nbsp;
177 | 		<a class="btn btn-primary" href="#timepicker4Modal" data-toggle="modal">Open Modal</a>
178 |    <a href="#timepicker4Source" data-toggle="collapse"> + View Source</a>
179 | 
180 |    <div id="timepicker4Source" class="collapse">
181 | <pre class="prettyprint linenums">
182 | &lt;!DOCTYPE html&gt;
183 | &lt;html&gt;
184 |     &lt;head&gt;
185 |         &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
186 |         &lt;link type="text/css" href="css/bootstrap.min.css" /&gt;
187 |         &lt;link type="text/css" href="css/bootstrap-timepicker.min.css" /&gt;
188 |         &lt;script type="text/javascript" src="js/jquery.min.js"&gt;&lt;/script&gt;
189 |         &lt;script type="text/javascript" src="js/bootstrap.min.js"&gt;&lt;/script&gt;
190 |         &lt;script type="text/javascript" src="js/bootstrap-timepicker.min.js"&gt;&lt;/script&gt;
191 |     &lt;/head&gt;
192 |     &lt;body&gt;
193 | 				&lt;div&gt; class="modal hide fade"&gt;
194 | 					&lt;div class="modal-header"&gt;
195 | 						&lt;h1&gt;Timepicker inside a modal&lt;/h1&gt;
196 | 					&lt;/div&gt;
197 | 					&lt;div class="modal-body"&gt;
198 | 						&lt;div class="input-group bootstrap-timepicker timepicker"&gt;
199 | 								&lt;input id="timepicker4" type="text" value="10:35 AM" class="form-control input-small"&gt;
200 | 								&lt;span class="input-group-addon"&gt;&lt;i class="glyphicon glyphicon-time"&gt;&lt;/i&gt;&lt;/span&gt;
201 | 						&lt;/div&gt;
202 | 					&lt;/div&gt;
203 | 				&lt;/div&gt;
204 | 
205 |         &lt;script type="text/javascript"&gt;
206 |             $('#timepicker4').timepicker({
207 |                 minuteStep: 1,
208 |                 secondStep: 5,
209 |                 showInputs: false,
210 |                 template: 'modal',
211 |                 modalBackdrop: true,
212 |                 showSeconds: true,
213 |                 showMeridian: false
214 |             });
215 |         &lt;/script&gt;
216 |     &lt;/body&gt;
217 | &lt;/html&gt;
218 | </pre>
219 |    </div>
220 |    <hr class="soften">
221 |    <p>Without a template.</p>
222 |    <span class="input-group bootstrap-timepicker timepicker">
223 |        <input id="timepicker5" class="form-control input-small" type="text" />
224 |        <i class="glyphicon glyphicon-time" style="margin: -2px 0 0 -22.5px; pointer-events: none; position: relative;"></i>
225 |    </span>
226 |     &nbsp;
227 |     <a href="#timepicker5Source" data-toggle="collapse"> + View Source</a>
228 |    <div id="timepicker5Source" class="collapse">
229 | <pre class="prettyprint linenums">
230 | &lt;!DOCTYPE html&gt;
231 | &lt;html&gt;
232 |     &lt;head&gt;
233 |         &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
234 |         &lt;link type="text/css" href="css/bootstrap.min.css" /&gt;
235 |         &lt;link type="text/css" href="css/bootstrap-timepicker.min.css" /&gt;
236 |         &lt;script type="text/javascript" src="js/jquery.min.js"&gt;&lt;/script&gt;
237 |         &lt;script type="text/javascript" src="js/bootstrap.min.js"&gt;&lt;/script&gt;
238 |         &lt;script type="text/javascript" src="js/bootstrap-timepicker.min.js"&gt;&lt;/script&gt;
239 |     &lt;/head&gt;
240 |     &lt;body&gt;
241 |         &lt;div class="bootstrap-timepicker"&gt;
242 |             &lt;input id="timepicker5" type="text" class="input-small"&gt;
243 |             &lt;i class="icon-time"&gt;&lt;/i&gt;
244 |         &lt;/div&gt;
245 | 
246 |         &lt;script type="text/javascript"&gt;
247 |             $('#timepicker5').timepicker({
248 |                 template: false,
249 |                 showInputs: false,
250 |                 minuteStep: 5
251 |             });
252 |         &lt;/script&gt;
253 |     &lt;/body&gt;
254 | &lt;/html&gt;
255 | </pre>
256 |     </div>
257 |     <hr>
258 |     <h1>Configuration</h1>
259 |     <hr>
260 |     <h3>template <small>The picker widget template</small></h3>
261 |     <div class="well">
262 |         <table class="options-table table table-bordered table-striped table-rounded">
263 |             <thead>
264 |                 <tr>
265 |                     <th style="width: 25%;">Name</th>
266 |                     <th style="width: 25%;">Options / Defaults</th>
267 |                     <th style="widget: 50%;">Description</th>
268 |                 </tr>
269 |             </thead>
270 |             <tbody>
271 |                 <tr>
272 |                     <td>template</td>
273 |                     <td>
274 |                         'dropdown' (default)<hr />
275 |                         'modal' <hr />
276 |                         false
277 |                     </td>
278 |                     <td>
279 |                         Show picker in a dropdown <hr />
280 |                         Show picker in a modal <hr />
281 |                         Don't show a widget
282 |                     </td>
283 |                 </tr>
284 |                 <tr>
285 |                     <td>maxHours</td>
286 |                     <td>24</td>
287 |                     <td>Specify a maximum number of hours the TimePicker can handle. showMeridian must be set to false</td>
288 |                 </tr>
289 |                 <tr>
290 |                     <td>snapToStep</td>
291 |                     <td>false</td>
292 |                     <td>If true, setting the time will snap it to the closest "step", either minute or second, depending on which unit is currently highlighted. If the number would otherwise snap to 60 higher, the unit "overflows" to 0.</td>
293 |                 </tr>
294 |                 <tr>
295 |                     <td>minuteStep</td>
296 |                     <td>15</td>
297 |                     <td>Specify a step for the minute field.</td>
298 |                 </tr>
299 |                 <tr>
300 |                     <td>showSeconds</td>
301 |                     <td>false</td>
302 |                     <td>Show the seconds field.</td>
303 |                 </tr>
304 |                 <tr>
305 |                     <td>secondStep</td>
306 |                     <td>15</td>
307 |                     <td>Specify a step for the second field.</td>
308 |                 </tr>
309 |                 <tr>
310 |                     <td>defaultTime</td>
311 |                     <td>
312 |                         'current' (default) <hr />
313 |                         '11:45 AM' <hr />
314 |                         false
315 |                     </td>
316 |                     <td>
317 |                         Set to the current time. <hr />
318 |                         Set to a specific time. <hr />
319 |                         Do not set a default time
320 |                     </td>
321 |                 </tr>
322 |                 <tr>
323 |                     <td>showMeridian</td>
324 |                     <td>
325 |                         true (default) <hr />
326 |                         false
327 |                     </td>
328 |                     <td>
329 |                         12hr mode<hr />
330 |                         24hr mode
331 |                     </td>
332 |                 </tr>
333 |                 <tr>
334 |                     <td>showInputs</td>
335 |                     <td>
336 |                         true (default) <hr />
337 |                         false
338 |                     </td>
339 |                     <td>
340 |                         Shows the text inputs in the widget. <hr />
341 |                         Hide the text inputs in the widget
342 |                     </td>
343 |                 </tr>
344 |                 <tr>
345 |                     <td>disableFocus</td>
346 |                     <td>false</td>
347 |                     <td>Disables the input from focusing. This is useful for touch screen devices that display a keyboard on input focus.</td>
348 |                 </tr>
349 |                 <tr>
350 |                     <td>disableMousewheel</td>
351 |                     <td>false</td>
352 |                     <td>Disables the input from changing on mousewheel events</td>
353 |                 </tr>
354 |                 <tr>
355 |                     <td>modalBackdrop</td>
356 |                     <td>false</td>
357 |                     <td>Show modal backdrop.</td>
358 |                 </tr>
359 |                 <tr>
360 |                     <td>appendWidgetTo</td>
361 |                     <td>body</td>
362 |                     <td>Allow for custom placing of the widget</td>
363 |                 </tr>
364 |                 <tr>
365 |                     <td>explicitMode</td>
366 |                     <td>false</td>
367 |                     <td>When true, user can type "123" to set time to "1:23" or "12345" to set time to "1:23:45".</td>
368 |                 </tr>
369 |                 <tr>
370 |                     <td>icons</td>
371 |                     <td>
372 |                         <pre><code>
373 | {
374 |     up: 'glyphicon glyphicon-chevron-up',
375 |     down: 'glyphicon glyphicon-chevron-down'
376 | }
377 | </code></pre>
378 |                     </td>
379 |                     <td>An object with keys 'up' and 'down' representing CSS class names to be used for the widget's icon classes.</td>
380 |                 </tr>
381 |             </tbody>
382 |         </table>
383 |     </div>
384 |     <div>
385 |         <h3>Data Attributes</h3>
386 |         <p>The timepicker can be instantiated lazily without using javascript using <code>data-provide="timepicker"</code>.</p>
387 | <pre class="prettyprint linenums">
388 |   &lt;div class="input-group bootstrap-timepicker timepicker"&gt;&lt;input id="timepicker" class="form-control" data-provide="timepicker" data-template="modal" data-minute-step="1" data-modal-backdrop="true" type="text"/&gt;&lt;/div&gt;
389 | </pre>
390 |         <p>Configuration options can also be set with the use of data attributes. </p>
391 | <pre class="prettyprint linenums">
392 |   &lt;div class="input-group bootstrap-timepicker timepicker"&gt;&lt;input id="timepicker" class="form-control" data-template="modal" data-minute-step="1" data-modal-backdrop="true" type="text"/&gt;&lt;/div&gt;
393 | </pre>
394 |     </div>
395 |     <hr>
396 |     <h1>Methods</h1>
397 |     <hr>
398 |     <h3>showWidget <small>Show the picker widget</small></h3>
399 | <pre class="prettyprint linenums">
400 |   var time = $('#timepicker').timepicker('showWidget');
401 | </pre>
402 |     <h3>setTime <small>Set the time manually</small></h3>
403 | <pre class="prettyprint linenums">
404 |   $('#timepicker').timepicker('setTime', '12:45 AM');
405 | </pre>
406 |     <hr>
407 |     <h1>Events</h1>
408 |     <hr>
409 |     <div>
410 |         <h3> Show <small>Triggered when dropdown/modal widget is shown</small></h3>
411 | <pre class="prettyprint linenums">
412 |   $('#timepicker').timepicker().on('show.timepicker', function(e) {
413 |     console.log('The time is ' + e.time.value);
414 |     console.log('The hour is ' + e.time.hours);
415 |     console.log('The minute is ' + e.time.minutes);
416 |     console.log('The meridian is ' + e.time.meridian);
417 |   });
418 | </pre>
419 |         <h3>Hide <small>Triggered when widget is hidden</small></h3>
420 | <pre class="prettyprint linenums">
421 |   $('#timepicker').timepicker().on('hide.timepicker', function(e) {
422 |     console.log('The time is ' + e.time.value);
423 |     console.log('The hour is ' + e.time.hours);
424 |     console.log('The minute is ' + e.time.minutes);
425 |     console.log('The meridian is ' + e.time.meridian);
426 |   });
427 | </pre>
428 |         <h3>Update <small>Triggered when the date is updated</small></h3>
429 | <pre class="prettyprint linenums">
430 |   $('#timepicker').timepicker().on('changeTime.timepicker', function(e) {
431 |     console.log('The time is ' + e.time.value);
432 |     console.log('The hour is ' + e.time.hours);
433 |     console.log('The minute is ' + e.time.minutes);
434 |     console.log('The meridian is ' + e.time.meridian);
435 |   });
436 | </pre>
437 | <hr>
438 |     </div>
439 | </section>
440 | 


--------------------------------------------------------------------------------
/js/bootstrap-timepicker.js:
--------------------------------------------------------------------------------
   1 | /*!
   2 |  * Timepicker Component for Twitter Bootstrap
   3 |  *
   4 |  * Copyright 2013 Joris de Wit and bootstrap-timepicker contributors
   5 |  *
   6 |  * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
   7 |  *
   8 |  * For the full copyright and license information, please view the LICENSE
   9 |  * file that was distributed with this source code.
  10 |  */
  11 | (function($, window, document) {
  12 |   'use strict';
  13 | 
  14 |   // TIMEPICKER PUBLIC CLASS DEFINITION
  15 |   var Timepicker = function(element, options) {
  16 |     this.widget = '';
  17 |     this.$element = $(element);
  18 |     this.defaultTime = options.defaultTime;
  19 |     this.disableFocus = options.disableFocus;
  20 |     this.disableMousewheel = options.disableMousewheel;
  21 |     this.isOpen = options.isOpen;
  22 |     this.minuteStep = options.minuteStep;
  23 |     this.modalBackdrop = options.modalBackdrop;
  24 |     this.orientation = options.orientation;
  25 |     this.secondStep = options.secondStep;
  26 |     this.snapToStep = options.snapToStep;
  27 |     this.showInputs = options.showInputs;
  28 |     this.showMeridian = options.showMeridian;
  29 |     this.showSeconds = options.showSeconds;
  30 |     this.template = options.template;
  31 |     this.appendWidgetTo = options.appendWidgetTo;
  32 |     this.showWidgetOnAddonClick = options.showWidgetOnAddonClick;
  33 |     this.icons = options.icons;
  34 |     this.maxHours = options.maxHours;
  35 |     this.explicitMode = options.explicitMode; // If true 123 = 1:23, 12345 = 1:23:45, else invalid.
  36 | 
  37 |     this.handleDocumentClick = function (e) {
  38 |       var self = e.data.scope;
  39 |       // This condition was inspired by bootstrap-datepicker.
  40 |       // The element the timepicker is invoked on is the input but it has a sibling for addon/button.
  41 |       if (!(self.$element.parent().find(e.target).length ||
  42 |           self.$widget.is(e.target) ||
  43 |           self.$widget.find(e.target).length)) {
  44 |         self.hideWidget();
  45 |       }
  46 |     };
  47 | 
  48 |     this._init();
  49 |   };
  50 | 
  51 |   Timepicker.prototype = {
  52 | 
  53 |     constructor: Timepicker,
  54 |     _init: function() {
  55 |       var self = this;
  56 | 
  57 |       if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-group') && this.$element.parent().hasClass('bootstrap-timepicker'))) {
  58 |         this.$element.parent('.input-group.bootstrap-timepicker').find('.input-group-addon').on({
  59 |           'click.timepicker': $.proxy(this.showWidget, this)
  60 |         });
  61 |         this.$element.on({
  62 |           'focus.timepicker': $.proxy(this.highlightUnit, this),
  63 |           'click.timepicker': $.proxy(this.highlightUnit, this),
  64 |           'keydown.timepicker': $.proxy(this.elementKeydown, this),
  65 |           'blur.timepicker': $.proxy(this.blurElement, this),
  66 |           'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
  67 |         });
  68 |       } else {
  69 |         if (this.template) {
  70 |           this.$element.on({
  71 |             'focus.timepicker': $.proxy(this.showWidget, this),
  72 |             'click.timepicker': $.proxy(this.showWidget, this),
  73 |             'blur.timepicker': $.proxy(this.blurElement, this),
  74 |             'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
  75 |           });
  76 |         } else {
  77 |           this.$element.on({
  78 |             'focus.timepicker': $.proxy(this.highlightUnit, this),
  79 |             'click.timepicker': $.proxy(this.highlightUnit, this),
  80 |             'keydown.timepicker': $.proxy(this.elementKeydown, this),
  81 |             'blur.timepicker': $.proxy(this.blurElement, this),
  82 |             'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
  83 |           });
  84 |         }
  85 |       }
  86 | 
  87 |       if (this.template !== false) {
  88 |         this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this));
  89 |       } else {
  90 |         this.$widget = false;
  91 |       }
  92 | 
  93 |       if (this.showInputs && this.$widget !== false) {
  94 |         this.$widget.find('input').each(function() {
  95 |           $(this).on({
  96 |             'click.timepicker': function() { $(this).select(); },
  97 |             'keydown.timepicker': $.proxy(self.widgetKeydown, self),
  98 |             'keyup.timepicker': $.proxy(self.widgetKeyup, self)
  99 |           });
 100 |         });
 101 |       }
 102 | 
 103 |       this.setDefaultTime(this.defaultTime);
 104 |     },
 105 | 
 106 |     blurElement: function() {
 107 |       this.highlightedUnit = null;
 108 |       this.updateFromElementVal();
 109 |     },
 110 | 
 111 |     clear: function() {
 112 |       this.hour = '';
 113 |       this.minute = '';
 114 |       this.second = '';
 115 |       this.meridian = '';
 116 | 
 117 |       this.$element.val('');
 118 |     },
 119 | 
 120 |     decrementHour: function() {
 121 |       if (this.showMeridian) {
 122 |         if (this.hour === 1) {
 123 |           this.hour = 12;
 124 |         } else if (this.hour === 12) {
 125 |           this.hour--;
 126 | 
 127 |           return this.toggleMeridian();
 128 |         } else if (this.hour === 0) {
 129 |           this.hour = 11;
 130 | 
 131 |           return this.toggleMeridian();
 132 |         } else {
 133 |           this.hour--;
 134 |         }
 135 |       } else {
 136 |         if (this.hour <= 0) {
 137 |           this.hour = this.maxHours - 1;
 138 |         } else {
 139 |           this.hour--;
 140 |         }
 141 |       }
 142 |     },
 143 | 
 144 |     decrementMinute: function(step) {
 145 |       var newVal;
 146 | 
 147 |       if (step) {
 148 |         newVal = this.minute - step;
 149 |       } else {
 150 |         newVal = this.minute - this.minuteStep;
 151 |       }
 152 | 
 153 |       if (newVal < 0) {
 154 |         this.decrementHour();
 155 |         this.minute = newVal + 60;
 156 |       } else {
 157 |         this.minute = newVal;
 158 |       }
 159 |     },
 160 | 
 161 |     decrementSecond: function() {
 162 |       var newVal = this.second - this.secondStep;
 163 | 
 164 |       if (newVal < 0) {
 165 |         this.decrementMinute(true);
 166 |         this.second = newVal + 60;
 167 |       } else {
 168 |         this.second = newVal;
 169 |       }
 170 |     },
 171 | 
 172 |     elementKeydown: function(e) {
 173 |       switch (e.which) {
 174 |       case 9: //tab
 175 |         if (e.shiftKey) {
 176 |           if (this.highlightedUnit === 'hour') {
 177 |             this.hideWidget();
 178 |             break;
 179 |           }
 180 |           this.highlightPrevUnit();
 181 |         } else if ((this.showMeridian && this.highlightedUnit === 'meridian') || (this.showSeconds && this.highlightedUnit === 'second') || (!this.showMeridian && !this.showSeconds && this.highlightedUnit ==='minute')) {
 182 |           this.hideWidget();
 183 |           break;
 184 |         } else {
 185 |           this.highlightNextUnit();
 186 |         }
 187 |         e.preventDefault();
 188 |         this.updateFromElementVal();
 189 |         break;
 190 |       case 27: // escape
 191 |         this.updateFromElementVal();
 192 |         break;
 193 |       case 37: // left arrow
 194 |         e.preventDefault();
 195 |         this.highlightPrevUnit();
 196 |         this.updateFromElementVal();
 197 |         break;
 198 |       case 38: // up arrow
 199 |         e.preventDefault();
 200 |         switch (this.highlightedUnit) {
 201 |         case 'hour':
 202 |           this.incrementHour();
 203 |           this.highlightHour();
 204 |           break;
 205 |         case 'minute':
 206 |           this.incrementMinute();
 207 |           this.highlightMinute();
 208 |           break;
 209 |         case 'second':
 210 |           this.incrementSecond();
 211 |           this.highlightSecond();
 212 |           break;
 213 |         case 'meridian':
 214 |           this.toggleMeridian();
 215 |           this.highlightMeridian();
 216 |           break;
 217 |         }
 218 |         this.update();
 219 |         break;
 220 |       case 39: // right arrow
 221 |         e.preventDefault();
 222 |         this.highlightNextUnit();
 223 |         this.updateFromElementVal();
 224 |         break;
 225 |       case 40: // down arrow
 226 |         e.preventDefault();
 227 |         switch (this.highlightedUnit) {
 228 |         case 'hour':
 229 |           this.decrementHour();
 230 |           this.highlightHour();
 231 |           break;
 232 |         case 'minute':
 233 |           this.decrementMinute();
 234 |           this.highlightMinute();
 235 |           break;
 236 |         case 'second':
 237 |           this.decrementSecond();
 238 |           this.highlightSecond();
 239 |           break;
 240 |         case 'meridian':
 241 |           this.toggleMeridian();
 242 |           this.highlightMeridian();
 243 |           break;
 244 |         }
 245 | 
 246 |         this.update();
 247 |         break;
 248 |       }
 249 |     },
 250 | 
 251 |     getCursorPosition: function() {
 252 |       var input = this.$element.get(0);
 253 | 
 254 |       if ('selectionStart' in input) {// Standard-compliant browsers
 255 | 
 256 |         return input.selectionStart;
 257 |       } else if (document.selection) {// IE fix
 258 |         input.focus();
 259 |         var sel = document.selection.createRange(),
 260 |           selLen = document.selection.createRange().text.length;
 261 | 
 262 |         sel.moveStart('character', - input.value.length);
 263 | 
 264 |         return sel.text.length - selLen;
 265 |       }
 266 |     },
 267 | 
 268 |     getTemplate: function() {
 269 |       var template,
 270 |         hourTemplate,
 271 |         minuteTemplate,
 272 |         secondTemplate,
 273 |         meridianTemplate,
 274 |         templateContent;
 275 | 
 276 |       if (this.showInputs) {
 277 |         hourTemplate = '<input type="text" class="bootstrap-timepicker-hour" maxlength="2"/>';
 278 |         minuteTemplate = '<input type="text" class="bootstrap-timepicker-minute" maxlength="2"/>';
 279 |         secondTemplate = '<input type="text" class="bootstrap-timepicker-second" maxlength="2"/>';
 280 |         meridianTemplate = '<input type="text" class="bootstrap-timepicker-meridian" maxlength="2"/>';
 281 |       } else {
 282 |         hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
 283 |         minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
 284 |         secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
 285 |         meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
 286 |       }
 287 | 
 288 |       templateContent = '<table>'+
 289 |          '<tr>'+
 290 |            '<td><a href="#" data-action="incrementHour"><span class="'+ this.icons.up +'"></span></a></td>'+
 291 |            '<td class="separator">&nbsp;</td>'+
 292 |            '<td><a href="#" data-action="incrementMinute"><span class="'+ this.icons.up +'"></span></a></td>'+
 293 |            (this.showSeconds ?
 294 |              '<td class="separator">&nbsp;</td>'+
 295 |              '<td><a href="#" data-action="incrementSecond"><span class="'+ this.icons.up +'"></span></a></td>'
 296 |            : '') +
 297 |            (this.showMeridian ?
 298 |              '<td class="separator">&nbsp;</td>'+
 299 |              '<td class="meridian-column"><a href="#" data-action="toggleMeridian"><span class="'+ this.icons.up +'"></span></a></td>'
 300 |            : '') +
 301 |          '</tr>'+
 302 |          '<tr>'+
 303 |            '<td>'+ hourTemplate +'</td> '+
 304 |            '<td class="separator">:</td>'+
 305 |            '<td>'+ minuteTemplate +'</td> '+
 306 |            (this.showSeconds ?
 307 |             '<td class="separator">:</td>'+
 308 |             '<td>'+ secondTemplate +'</td>'
 309 |            : '') +
 310 |            (this.showMeridian ?
 311 |             '<td class="separator">&nbsp;</td>'+
 312 |             '<td>'+ meridianTemplate +'</td>'
 313 |            : '') +
 314 |          '</tr>'+
 315 |          '<tr>'+
 316 |            '<td><a href="#" data-action="decrementHour"><span class="'+ this.icons.down +'"></span></a></td>'+
 317 |            '<td class="separator"></td>'+
 318 |            '<td><a href="#" data-action="decrementMinute"><span class="'+ this.icons.down +'"></span></a></td>'+
 319 |            (this.showSeconds ?
 320 |             '<td class="separator">&nbsp;</td>'+
 321 |             '<td><a href="#" data-action="decrementSecond"><span class="'+ this.icons.down +'"></span></a></td>'
 322 |            : '') +
 323 |            (this.showMeridian ?
 324 |             '<td class="separator">&nbsp;</td>'+
 325 |             '<td><a href="#" data-action="toggleMeridian"><span class="'+ this.icons.down +'"></span></a></td>'
 326 |            : '') +
 327 |          '</tr>'+
 328 |        '</table>';
 329 | 
 330 |       switch(this.template) {
 331 |       case 'modal':
 332 |         template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
 333 |           '<div class="modal-header">'+
 334 |             '<a href="#" class="close" data-dismiss="modal">&times;</a>'+
 335 |             '<h3>Pick a Time</h3>'+
 336 |           '</div>'+
 337 |           '<div class="modal-content">'+
 338 |             templateContent +
 339 |           '</div>'+
 340 |           '<div class="modal-footer">'+
 341 |             '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
 342 |           '</div>'+
 343 |         '</div>';
 344 |         break;
 345 |       case 'dropdown':
 346 |         template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
 347 |         break;
 348 |       }
 349 | 
 350 |       return template;
 351 |     },
 352 | 
 353 |     getTime: function() {
 354 |       if (this.hour === '') {
 355 |         return '';
 356 |       }
 357 | 
 358 |       return this.hour + ':' + (this.minute.toString().length === 1 ? '0' + this.minute : this.minute) + (this.showSeconds ? ':' + (this.second.toString().length === 1 ? '0' + this.second : this.second) : '') + (this.showMeridian ? ' ' + this.meridian : '');
 359 |     },
 360 | 
 361 |     hideWidget: function() {
 362 |       if (this.isOpen === false) {
 363 |         return;
 364 |       }
 365 | 
 366 |       this.$element.trigger({
 367 |         'type': 'hide.timepicker',
 368 |         'time': {
 369 |           'value': this.getTime(),
 370 |           'hours': this.hour,
 371 |           'minutes': this.minute,
 372 |           'seconds': this.second,
 373 |           'meridian': this.meridian
 374 |         }
 375 |       });
 376 | 
 377 |       if (this.template === 'modal' && this.$widget.modal) {
 378 |         this.$widget.modal('hide');
 379 |       } else {
 380 |         this.$widget.removeClass('open');
 381 |       }
 382 | 
 383 |       $(document).off('mousedown.timepicker, touchend.timepicker', this.handleDocumentClick);
 384 | 
 385 |       this.isOpen = false;
 386 |       // show/hide approach taken by datepicker
 387 |       this.$widget.detach();
 388 |     },
 389 | 
 390 |     highlightUnit: function() {
 391 |       this.position = this.getCursorPosition();
 392 |       if (this.position >= 0 && this.position <= 2) {
 393 |         this.highlightHour();
 394 |       } else if (this.position >= 3 && this.position <= 5) {
 395 |         this.highlightMinute();
 396 |       } else if (this.position >= 6 && this.position <= 8) {
 397 |         if (this.showSeconds) {
 398 |           this.highlightSecond();
 399 |         } else {
 400 |           this.highlightMeridian();
 401 |         }
 402 |       } else if (this.position >= 9 && this.position <= 11) {
 403 |         this.highlightMeridian();
 404 |       }
 405 |     },
 406 | 
 407 |     highlightNextUnit: function() {
 408 |       switch (this.highlightedUnit) {
 409 |       case 'hour':
 410 |         this.highlightMinute();
 411 |         break;
 412 |       case 'minute':
 413 |         if (this.showSeconds) {
 414 |           this.highlightSecond();
 415 |         } else if (this.showMeridian){
 416 |           this.highlightMeridian();
 417 |         } else {
 418 |           this.highlightHour();
 419 |         }
 420 |         break;
 421 |       case 'second':
 422 |         if (this.showMeridian) {
 423 |           this.highlightMeridian();
 424 |         } else {
 425 |           this.highlightHour();
 426 |         }
 427 |         break;
 428 |       case 'meridian':
 429 |         this.highlightHour();
 430 |         break;
 431 |       }
 432 |     },
 433 | 
 434 |     highlightPrevUnit: function() {
 435 |       switch (this.highlightedUnit) {
 436 |       case 'hour':
 437 |         if(this.showMeridian){
 438 |           this.highlightMeridian();
 439 |         } else if (this.showSeconds) {
 440 |           this.highlightSecond();
 441 |         } else {
 442 |           this.highlightMinute();
 443 |         }
 444 |         break;
 445 |       case 'minute':
 446 |         this.highlightHour();
 447 |         break;
 448 |       case 'second':
 449 |         this.highlightMinute();
 450 |         break;
 451 |       case 'meridian':
 452 |         if (this.showSeconds) {
 453 |           this.highlightSecond();
 454 |         } else {
 455 |           this.highlightMinute();
 456 |         }
 457 |         break;
 458 |       }
 459 |     },
 460 | 
 461 |     highlightHour: function() {
 462 |       var $element = this.$element.get(0),
 463 |           self = this;
 464 | 
 465 |       this.highlightedUnit = 'hour';
 466 | 
 467 |       if ($element.setSelectionRange) {
 468 |         setTimeout(function() {
 469 |           if (self.hour < 10) {
 470 |             $element.setSelectionRange(0,1);
 471 |           } else {
 472 |             $element.setSelectionRange(0,2);
 473 |           }
 474 |         }, 0);
 475 |       }
 476 |     },
 477 | 
 478 |     highlightMinute: function() {
 479 |       var $element = this.$element.get(0),
 480 |           self = this;
 481 | 
 482 |       this.highlightedUnit = 'minute';
 483 | 
 484 |       if ($element.setSelectionRange) {
 485 |         setTimeout(function() {
 486 |           if (self.hour < 10) {
 487 |             $element.setSelectionRange(2,4);
 488 |           } else {
 489 |             $element.setSelectionRange(3,5);
 490 |           }
 491 |         }, 0);
 492 |       }
 493 |     },
 494 | 
 495 |     highlightSecond: function() {
 496 |       var $element = this.$element.get(0),
 497 |           self = this;
 498 | 
 499 |       this.highlightedUnit = 'second';
 500 | 
 501 |       if ($element.setSelectionRange) {
 502 |         setTimeout(function() {
 503 |           if (self.hour < 10) {
 504 |             $element.setSelectionRange(5,7);
 505 |           } else {
 506 |             $element.setSelectionRange(6,8);
 507 |           }
 508 |         }, 0);
 509 |       }
 510 |     },
 511 | 
 512 |     highlightMeridian: function() {
 513 |       var $element = this.$element.get(0),
 514 |           self = this;
 515 | 
 516 |       this.highlightedUnit = 'meridian';
 517 | 
 518 |       if ($element.setSelectionRange) {
 519 |         if (this.showSeconds) {
 520 |           setTimeout(function() {
 521 |             if (self.hour < 10) {
 522 |               $element.setSelectionRange(8,10);
 523 |             } else {
 524 |               $element.setSelectionRange(9,11);
 525 |             }
 526 |           }, 0);
 527 |         } else {
 528 |           setTimeout(function() {
 529 |             if (self.hour < 10) {
 530 |               $element.setSelectionRange(5,7);
 531 |             } else {
 532 |               $element.setSelectionRange(6,8);
 533 |             }
 534 |           }, 0);
 535 |         }
 536 |       }
 537 |     },
 538 | 
 539 |     incrementHour: function() {
 540 |       if (this.showMeridian) {
 541 |         if (this.hour === 11) {
 542 |           this.hour++;
 543 |           return this.toggleMeridian();
 544 |         } else if (this.hour === 12) {
 545 |           this.hour = 0;
 546 |         }
 547 |       }
 548 |       if (this.hour === this.maxHours - 1) {
 549 |         this.hour = 0;
 550 | 
 551 |         return;
 552 |       }
 553 |       this.hour++;
 554 |     },
 555 | 
 556 |     incrementMinute: function(step) {
 557 |       var newVal;
 558 | 
 559 |       if (step) {
 560 |         newVal = this.minute + step;
 561 |       } else {
 562 |         newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
 563 |       }
 564 | 
 565 |       if (newVal > 59) {
 566 |         this.incrementHour();
 567 |         this.minute = newVal - 60;
 568 |       } else {
 569 |         this.minute = newVal;
 570 |       }
 571 |     },
 572 | 
 573 |     incrementSecond: function() {
 574 |       var newVal = this.second + this.secondStep - (this.second % this.secondStep);
 575 | 
 576 |       if (newVal > 59) {
 577 |         this.incrementMinute(true);
 578 |         this.second = newVal - 60;
 579 |       } else {
 580 |         this.second = newVal;
 581 |       }
 582 |     },
 583 | 
 584 |     mousewheel: function(e) {
 585 |       if (this.disableMousewheel) {
 586 |         return;
 587 |       }
 588 | 
 589 |       e.preventDefault();
 590 |       e.stopPropagation();
 591 | 
 592 |       var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail,
 593 |           scrollTo = null;
 594 | 
 595 |       if (e.type === 'mousewheel') {
 596 |         scrollTo = (e.originalEvent.wheelDelta * -1);
 597 |       }
 598 |       else if (e.type === 'DOMMouseScroll') {
 599 |         scrollTo = 40 * e.originalEvent.detail;
 600 |       }
 601 | 
 602 |       if (scrollTo) {
 603 |         e.preventDefault();
 604 |         $(this).scrollTop(scrollTo + $(this).scrollTop());
 605 |       }
 606 | 
 607 |       switch (this.highlightedUnit) {
 608 |       case 'minute':
 609 |         if (delta > 0) {
 610 |           this.incrementMinute();
 611 |         } else {
 612 |           this.decrementMinute();
 613 |         }
 614 |         this.highlightMinute();
 615 |         break;
 616 |       case 'second':
 617 |         if (delta > 0) {
 618 |           this.incrementSecond();
 619 |         } else {
 620 |           this.decrementSecond();
 621 |         }
 622 |         this.highlightSecond();
 623 |         break;
 624 |       case 'meridian':
 625 |         this.toggleMeridian();
 626 |         this.highlightMeridian();
 627 |         break;
 628 |       default:
 629 |         if (delta > 0) {
 630 |           this.incrementHour();
 631 |         } else {
 632 |           this.decrementHour();
 633 |         }
 634 |         this.highlightHour();
 635 |         break;
 636 |       }
 637 | 
 638 |       return false;
 639 |     },
 640 | 
 641 |     /**
 642 |      * Given a segment value like 43, will round and snap the segment
 643 |      * to the nearest "step", like 45 if step is 15. Segment will
 644 |      * "overflow" to 0 if it's larger than 59 or would otherwise
 645 |      * round up to 60.
 646 |      */
 647 |     changeToNearestStep: function (segment, step) {
 648 |       if (segment % step === 0) {
 649 |         return segment;
 650 |       }
 651 |       if (Math.round((segment % step) / step)) {
 652 |         return (segment + (step - segment % step)) % 60;
 653 |       } else {
 654 |         return segment - segment % step;
 655 |       }
 656 |     },
 657 | 
 658 |     // This method was adapted from bootstrap-datepicker.
 659 |     place : function() {
 660 |       if (this.isInline) {
 661 |         return;
 662 |       }
 663 |       var widgetWidth = this.$widget.outerWidth(), widgetHeight = this.$widget.outerHeight(), visualPadding = 10, windowWidth =
 664 |         $(window).width(), windowHeight = $(window).height(), scrollTop = $(window).scrollTop();
 665 | 
 666 |       var zIndex = parseInt(this.$element.parents().filter(function() { return $(this).css('z-index') !== 'auto'; }).first().css('z-index'), 10) + 10;
 667 |       var offset = this.component ? this.component.parent().offset() : this.$element.offset();
 668 |       var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false);
 669 |       var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false);
 670 |       var left = offset.left, top = offset.top;
 671 | 
 672 |       this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left');
 673 | 
 674 |       if (this.orientation.x !== 'auto') {
 675 |         this.$widget.addClass('timepicker-orient-' + this.orientation.x);
 676 |         if (this.orientation.x === 'right') {
 677 |           left -= widgetWidth - width;
 678 |         }
 679 |       } else{
 680 |         // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways
 681 |         // Default to left
 682 |         this.$widget.addClass('timepicker-orient-left');
 683 |         if (offset.left < 0) {
 684 |           left -= offset.left - visualPadding;
 685 |         } else if (offset.left + widgetWidth > windowWidth) {
 686 |           left = windowWidth - widgetWidth - visualPadding;
 687 |         }
 688 |       }
 689 |       // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget
 690 |       var yorient = this.orientation.y, topOverflow, bottomOverflow;
 691 |       if (yorient === 'auto') {
 692 |         topOverflow = -scrollTop + offset.top - widgetHeight;
 693 |         bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight);
 694 |         if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) {
 695 |           yorient = 'top';
 696 |         } else {
 697 |           yorient = 'bottom';
 698 |         }
 699 |       }
 700 |       this.$widget.addClass('timepicker-orient-' + yorient);
 701 |       if (yorient === 'top'){
 702 |         top += height;
 703 |       } else{
 704 |         top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10);
 705 |       }
 706 | 
 707 |       this.$widget.css({
 708 |         top : top,
 709 |         left : left,
 710 |         zIndex : zIndex
 711 |       });
 712 |     },
 713 | 
 714 |     remove: function() {
 715 |       $('document').off('.timepicker');
 716 |       if (this.$widget) {
 717 |         this.$widget.remove();
 718 |       }
 719 |       delete this.$element.data().timepicker;
 720 |     },
 721 | 
 722 |     setDefaultTime: function(defaultTime) {
 723 |       if (!this.$element.val()) {
 724 |         if (defaultTime === 'current') {
 725 |           var dTime = new Date(),
 726 |             hours = dTime.getHours(),
 727 |             minutes = dTime.getMinutes(),
 728 |             seconds = dTime.getSeconds(),
 729 |             meridian = 'AM';
 730 | 
 731 |           if (seconds !== 0) {
 732 |             seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep;
 733 |             if (seconds === 60) {
 734 |               minutes += 1;
 735 |               seconds = 0;
 736 |             }
 737 |           }
 738 | 
 739 |           if (minutes !== 0) {
 740 |             minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
 741 |             if (minutes === 60) {
 742 |               hours += 1;
 743 |               minutes = 0;
 744 |             }
 745 |           }
 746 | 
 747 |           if (this.showMeridian) {
 748 |             if (hours === 0) {
 749 |               hours = 12;
 750 |             } else if (hours >= 12) {
 751 |               if (hours > 12) {
 752 |                 hours = hours - 12;
 753 |               }
 754 |               meridian = 'PM';
 755 |             } else {
 756 |               meridian = 'AM';
 757 |             }
 758 |           }
 759 | 
 760 |           this.hour = hours;
 761 |           this.minute = minutes;
 762 |           this.second = seconds;
 763 |           this.meridian = meridian;
 764 | 
 765 |           this.update();
 766 | 
 767 |         } else if (defaultTime === false) {
 768 |           this.hour = 0;
 769 |           this.minute = 0;
 770 |           this.second = 0;
 771 |           this.meridian = 'AM';
 772 |         } else {
 773 |           this.setTime(defaultTime);
 774 |         }
 775 |       } else {
 776 |         this.updateFromElementVal();
 777 |       }
 778 |     },
 779 | 
 780 |     setTime: function(time, ignoreWidget) {
 781 |       if (!time) {
 782 |         this.clear();
 783 |         return;
 784 |       }
 785 | 
 786 |       var timeMode,
 787 |           timeArray,
 788 |           hour,
 789 |           minute,
 790 |           second,
 791 |           meridian;
 792 | 
 793 |       if (typeof time === 'object' && time.getMonth){
 794 |         // this is a date object
 795 |         hour    = time.getHours();
 796 |         minute  = time.getMinutes();
 797 |         second  = time.getSeconds();
 798 | 
 799 |         if (this.showMeridian){
 800 |           meridian = 'AM';
 801 |           if (hour > 12){
 802 |             meridian = 'PM';
 803 |             hour = hour % 12;
 804 |           }
 805 | 
 806 |           if (hour === 12){
 807 |             meridian = 'PM';
 808 |           }
 809 |         }
 810 |       } else {
 811 |         timeMode = ((/a/i).test(time) ? 1 : 0) + ((/p/i).test(time) ? 2 : 0); // 0 = none, 1 = AM, 2 = PM, 3 = BOTH.
 812 |         if (timeMode > 2) { // If both are present, fail.
 813 |           this.clear();
 814 |           return;
 815 |         }
 816 | 
 817 |         timeArray = time.replace(/[^0-9\:]/g, '').split(':');
 818 | 
 819 |         hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
 820 | 
 821 |         if(this.explicitMode && hour.length > 2 && (hour.length % 2) !== 0 ) {
 822 |           this.clear();
 823 |           return;
 824 |         }
 825 | 
 826 |         minute = timeArray[1] ? timeArray[1].toString() : '';
 827 |         second = timeArray[2] ? timeArray[2].toString() : '';
 828 | 
 829 |         // adaptive time parsing
 830 |         if (hour.length > 4) {
 831 |           second = hour.slice(-2);
 832 |           hour = hour.slice(0, -2);
 833 |         }
 834 | 
 835 |         if (hour.length > 2) {
 836 |           minute = hour.slice(-2);
 837 |           hour = hour.slice(0, -2);
 838 |         }
 839 | 
 840 |         if (minute.length > 2) {
 841 |           second = minute.slice(-2);
 842 |           minute = minute.slice(0, -2);
 843 |         }
 844 | 
 845 |         hour = parseInt(hour, 10);
 846 |         minute = parseInt(minute, 10);
 847 |         second = parseInt(second, 10);
 848 | 
 849 |         if (isNaN(hour)) {
 850 |           hour = 0;
 851 |         }
 852 |         if (isNaN(minute)) {
 853 |           minute = 0;
 854 |         }
 855 |         if (isNaN(second)) {
 856 |           second = 0;
 857 |         }
 858 | 
 859 |         // Adjust the time based upon unit boundary.
 860 |         // NOTE: Negatives will never occur due to time.replace() above.
 861 |         if (second > 59) {
 862 |           second = 59;
 863 |         }
 864 | 
 865 |         if (minute > 59) {
 866 |           minute = 59;
 867 |         }
 868 | 
 869 |         if (hour >= this.maxHours) {
 870 |           // No day/date handling.
 871 |           hour = this.maxHours - 1;
 872 |         }
 873 | 
 874 |         if (this.showMeridian) {
 875 |           if (hour > 12) {
 876 |             // Force PM.
 877 |             timeMode = 2;
 878 |             hour -= 12;
 879 |           }
 880 |           if (!timeMode) {
 881 |             timeMode = 1;
 882 |           }
 883 |           if (hour === 0) {
 884 |             hour = 12; // AM or PM, reset to 12.  0 AM = 12 AM.  0 PM = 12 PM, etc.
 885 |           }
 886 |           meridian = timeMode === 1 ? 'AM' : 'PM';
 887 |         } else if (hour < 12 && timeMode === 2) {
 888 |           hour += 12;
 889 |         } else {
 890 |           if (hour >= this.maxHours) {
 891 |             hour = this.maxHours - 1;
 892 |           } else if ((hour < 0) || (hour === 12 && timeMode === 1)){
 893 |             hour = 0;
 894 |           }
 895 |         }
 896 |       }
 897 | 
 898 |       this.hour = hour;
 899 |       if (this.snapToStep) {
 900 |         this.minute = this.changeToNearestStep(minute, this.minuteStep);
 901 |         this.second = this.changeToNearestStep(second, this.secondStep);
 902 |       } else {
 903 |         this.minute = minute;
 904 |         this.second = second;
 905 |       }
 906 |       this.meridian = meridian;
 907 | 
 908 |       this.update(ignoreWidget);
 909 |     },
 910 | 
 911 |     showWidget: function() {
 912 |       if (this.isOpen) {
 913 |         return;
 914 |       }
 915 | 
 916 |       if (this.$element.is(':disabled')) {
 917 |         return;
 918 |       }
 919 | 
 920 |       // show/hide approach taken by datepicker
 921 |       this.$widget.appendTo(this.appendWidgetTo);
 922 |       $(document).on('mousedown.timepicker, touchend.timepicker', {scope: this}, this.handleDocumentClick);
 923 | 
 924 |       this.$element.trigger({
 925 |         'type': 'show.timepicker',
 926 |         'time': {
 927 |           'value': this.getTime(),
 928 |           'hours': this.hour,
 929 |           'minutes': this.minute,
 930 |           'seconds': this.second,
 931 |           'meridian': this.meridian
 932 |         }
 933 |       });
 934 | 
 935 |       this.place();
 936 |       if (this.disableFocus) {
 937 |         this.$element.blur();
 938 |       }
 939 | 
 940 |       // widget shouldn't be empty on open
 941 |       if (this.hour === '') {
 942 |         if (this.defaultTime) {
 943 |           this.setDefaultTime(this.defaultTime);
 944 |         } else {
 945 |           this.setTime('0:0:0');
 946 |         }
 947 |       }
 948 | 
 949 |       if (this.template === 'modal' && this.$widget.modal) {
 950 |         this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
 951 |       } else {
 952 |         if (this.isOpen === false) {
 953 |           this.$widget.addClass('open');
 954 |         }
 955 |       }
 956 | 
 957 |       this.isOpen = true;
 958 |     },
 959 | 
 960 |     toggleMeridian: function() {
 961 |       this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
 962 |     },
 963 | 
 964 |     update: function(ignoreWidget) {
 965 |       this.updateElement();
 966 |       if (!ignoreWidget) {
 967 |         this.updateWidget();
 968 |       }
 969 | 
 970 |       this.$element.trigger({
 971 |         'type': 'changeTime.timepicker',
 972 |         'time': {
 973 |           'value': this.getTime(),
 974 |           'hours': this.hour,
 975 |           'minutes': this.minute,
 976 |           'seconds': this.second,
 977 |           'meridian': this.meridian
 978 |         }
 979 |       });
 980 |     },
 981 | 
 982 |     updateElement: function() {
 983 |       this.$element.val(this.getTime()).change();
 984 |     },
 985 | 
 986 |     updateFromElementVal: function() {
 987 |       this.setTime(this.$element.val());
 988 |     },
 989 | 
 990 |     updateWidget: function() {
 991 |       if (this.$widget === false) {
 992 |         return;
 993 |       }
 994 | 
 995 |       var hour = this.hour,
 996 |           minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute,
 997 |           second = this.second.toString().length === 1 ? '0' + this.second : this.second;
 998 | 
 999 |       if (this.showInputs) {
1000 |         this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
1001 |         this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
1002 | 
1003 |         if (this.showSeconds) {
1004 |           this.$widget.find('input.bootstrap-timepicker-second').val(second);
1005 |         }
1006 |         if (this.showMeridian) {
1007 |           this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
1008 |         }
1009 |       } else {
1010 |         this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
1011 |         this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
1012 | 
1013 |         if (this.showSeconds) {
1014 |           this.$widget.find('span.bootstrap-timepicker-second').text(second);
1015 |         }
1016 |         if (this.showMeridian) {
1017 |           this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
1018 |         }
1019 |       }
1020 |     },
1021 | 
1022 |     updateFromWidgetInputs: function() {
1023 |       if (this.$widget === false) {
1024 |         return;
1025 |       }
1026 | 
1027 |       var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' +
1028 |               this.$widget.find('input.bootstrap-timepicker-minute').val() +
1029 |               (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') +
1030 |               (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '')
1031 |       ;
1032 | 
1033 |       this.setTime(t, true);
1034 |     },
1035 | 
1036 |     widgetClick: function(e) {
1037 |       e.stopPropagation();
1038 |       e.preventDefault();
1039 | 
1040 |       var $input = $(e.target),
1041 |           action = $input.closest('a').data('action');
1042 | 
1043 |       if (action) {
1044 |         this[action]();
1045 |       }
1046 |       this.update();
1047 | 
1048 |       if ($input.is('input')) {
1049 |         $input.get(0).setSelectionRange(0,2);
1050 |       }
1051 |     },
1052 | 
1053 |     widgetKeydown: function(e) {
1054 |       var $input = $(e.target),
1055 |           name = $input.attr('class').replace('bootstrap-timepicker-', '');
1056 | 
1057 |       switch (e.which) {
1058 |       case 9: //tab
1059 |         if (e.shiftKey) {
1060 |           if (name === 'hour') {
1061 |             return this.hideWidget();
1062 |           }
1063 |         } else if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) {
1064 |           return this.hideWidget();
1065 |         }
1066 |         break;
1067 |       case 27: // escape
1068 |         this.hideWidget();
1069 |         break;
1070 |       case 38: // up arrow
1071 |         e.preventDefault();
1072 |         switch (name) {
1073 |         case 'hour':
1074 |           this.incrementHour();
1075 |           break;
1076 |         case 'minute':
1077 |           this.incrementMinute();
1078 |           break;
1079 |         case 'second':
1080 |           this.incrementSecond();
1081 |           break;
1082 |         case 'meridian':
1083 |           this.toggleMeridian();
1084 |           break;
1085 |         }
1086 |         this.setTime(this.getTime());
1087 |         $input.get(0).setSelectionRange(0,2);
1088 |         break;
1089 |       case 40: // down arrow
1090 |         e.preventDefault();
1091 |         switch (name) {
1092 |         case 'hour':
1093 |           this.decrementHour();
1094 |           break;
1095 |         case 'minute':
1096 |           this.decrementMinute();
1097 |           break;
1098 |         case 'second':
1099 |           this.decrementSecond();
1100 |           break;
1101 |         case 'meridian':
1102 |           this.toggleMeridian();
1103 |           break;
1104 |         }
1105 |         this.setTime(this.getTime());
1106 |         $input.get(0).setSelectionRange(0,2);
1107 |         break;
1108 |       }
1109 |     },
1110 | 
1111 |     widgetKeyup: function(e) {
1112 |       if ((e.which === 65) || (e.which === 77) || (e.which === 80) || (e.which === 46) || (e.which === 8) || (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105)) {
1113 |         this.updateFromWidgetInputs();
1114 |       }
1115 |     }
1116 |   };
1117 | 
1118 |   //TIMEPICKER PLUGIN DEFINITION
1119 |   $.fn.timepicker = function(option) {
1120 |     var args = Array.apply(null, arguments);
1121 |     args.shift();
1122 |     return this.each(function() {
1123 |       var $this = $(this),
1124 |         data = $this.data('timepicker'),
1125 |         options = typeof option === 'object' && option;
1126 | 
1127 |       if (!data) {
1128 |         $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
1129 |       }
1130 | 
1131 |       if (typeof option === 'string') {
1132 |         data[option].apply(data, args);
1133 |       }
1134 |     });
1135 |   };
1136 | 
1137 |   $.fn.timepicker.defaults = {
1138 |     defaultTime: 'current',
1139 |     disableFocus: false,
1140 |     disableMousewheel: false,
1141 |     isOpen: false,
1142 |     minuteStep: 15,
1143 |     modalBackdrop: false,
1144 |     orientation: { x: 'auto', y: 'auto'},
1145 |     secondStep: 15,
1146 |     snapToStep: false,
1147 |     showSeconds: false,
1148 |     showInputs: true,
1149 |     showMeridian: true,
1150 |     template: 'dropdown',
1151 |     appendWidgetTo: 'body',
1152 |     showWidgetOnAddonClick: true,
1153 |     icons: {
1154 |       up: 'glyphicon glyphicon-chevron-up',
1155 |       down: 'glyphicon glyphicon-chevron-down'
1156 |     },
1157 |     maxHours: 24,
1158 |     explicitMode: false
1159 |   };
1160 | 
1161 |   $.fn.timepicker.Constructor = Timepicker;
1162 | 
1163 |   $(document).on(
1164 |     'focus.timepicker.data-api click.timepicker.data-api',
1165 |     '[data-provide="timepicker"]',
1166 |     function(e){
1167 |       var $this = $(this);
1168 |       if ($this.data('timepicker')) {
1169 |         return;
1170 |       }
1171 |       e.preventDefault();
1172 |       // component click requires us to explicitly show it
1173 |       $this.timepicker();
1174 |     }
1175 |   );
1176 | 
1177 | })(jQuery, window, document);
1178 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "bootstrap-timepicker",
 3 |   "description": "Timepicker widget for Twitter Bootstrap",
 4 |   "version": "0.5.2",
 5 |   "license": "MIT",
 6 |   "homepage": "http://jdewit.github.com/bootstrap-timepicker",
 7 |   "author": {
 8 |     "name": "Joris de Wit",
 9 |     "email": "joris.w.dewit@gmail.com",
10 |     "url": "http://jorisdewit.ca"
11 |   },
12 |   "main": "js/bootstrap-timepicker.js",
13 |   "files": ["js", "css"],
14 |   "repository": {
15 |     "type": "git",
16 |     "url": "git://github.com/jdewit/bootstrap-timepicker"
17 |   },
18 |   "scripts": {
19 |     "test": "bower install; grunt test;"
20 |   },
21 |   "bugs": {
22 |     "url": "https://github.com/jdewit/bootstrap-timepicker/issues"
23 |   },
24 |   "devDependencies": {
25 |     "grunt-cli": "~0.1",
26 |     "grunt": "~0.4.1",
27 |     "bower": "latest",
28 |     "grunt-contrib-less": "~0.5.0",
29 |     "grunt-contrib-jshint": "~0.4.3",
30 |     "grunt-contrib-uglify": "~0.2.0",
31 |     "grunt-contrib-jasmine": "~0.4.2",
32 |     "grunt-contrib-watch": "~0.4.3",
33 |     "grunt-shell": "~0.2.2",
34 |     "grunt-bump": "0.0.11"
35 |   }
36 | }
37 | 


--------------------------------------------------------------------------------
/spec/js/KeyboardEventsSpec.js:
--------------------------------------------------------------------------------
  1 | describe('Keyboard events feature', function() {
  2 |   'use strict';
  3 | 
  4 |   var $input1,
  5 |     $input2,
  6 |     $input3,
  7 |     $input5,
  8 |     $timepicker1,
  9 |     $timepicker2,
 10 |     $timepicker3,
 11 |     $timepicker5,
 12 |     tp1,
 13 |     tp2,
 14 |     tp3,
 15 |     tp5;
 16 | 
 17 |   beforeEach(function () {
 18 |     loadFixtures('timepicker.html');
 19 | 
 20 |     $input1 = $('#timepicker1');
 21 |     $timepicker1 = $input1.timepicker();
 22 |     tp1 = $timepicker1.data('timepicker');
 23 | 
 24 |     $input2 = $('#timepicker2');
 25 |     $timepicker2 = $input2.timepicker({
 26 |       template: 'modal',
 27 |       showSeconds: true,
 28 |       minuteStep: 30,
 29 |       secondStep: 30,
 30 |       defaultTime: false
 31 |     });
 32 |     tp2 = $timepicker2.data('timepicker');
 33 | 
 34 |     $input3 = $('#timepicker3');
 35 |     $timepicker3 = $input3.timepicker({
 36 |       defaultTime: '23:15:20',
 37 |       showMeridian: false,
 38 |       showSeconds: true,
 39 |       template: false
 40 |     });
 41 |     tp3 = $timepicker3.data('timepicker');
 42 | 
 43 |     $input5 = $('#timepicker-snapper');
 44 |     $timepicker5 = $input5.timepicker({
 45 |       snapToStep: true
 46 |     });
 47 |     tp5 = $timepicker5.data('timepicker');
 48 |   });
 49 | 
 50 |   afterEach(function () {
 51 |     $input1.data('timepicker').remove();
 52 |     $input2.data('timepicker').remove();
 53 |     $input3.data('timepicker').remove();
 54 |     $input5.data('timepicker').remove();
 55 |     $input1.remove();
 56 |     $input2.remove();
 57 |     $input3.remove();
 58 |     $input5.remove();
 59 |   });
 60 | 
 61 |   it('should be able to set time via input', function() {
 62 |     $input1.trigger('focus');
 63 |     expect(tp1.highlightedUnit).toBe('hour');
 64 |     $input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}9:45a{{tab}}');
 65 | 
 66 |     expect(tp1.highlightedUnit).toBe('minute');
 67 |     expect(tp1.getTime()).toBe('9:45 AM');
 68 |     expect($input1.is(':focus')).toBe(false);
 69 |   });
 70 | 
 71 |   it('should be able to control element by the arrow keys', function() {
 72 |     tp1.setTime('11:30 AM');
 73 |     tp1.update();
 74 | 
 75 |     $input1.trigger('focus');
 76 |     expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted by default');
 77 |     // hours
 78 |     $input1.autotype('{{up}}');
 79 |     expect(tp1.getTime()).toBe('12:30 PM', '1');
 80 |     $input1.autotype('{{down}}');
 81 |     expect(tp1.getTime()).toBe('11:30 AM', '2');
 82 |     expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted');
 83 | 
 84 |     $input1.autotype('{{right}}');
 85 |     expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted');
 86 | 
 87 |     //minutes
 88 |     $input1.autotype('{{up}}');
 89 |     expect(tp1.getTime()).toBe('11:45 AM', '3');
 90 |     expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 1');
 91 | 
 92 |     $input1.autotype('{{down}}');
 93 |     expect(tp1.getTime()).toBe('11:30 AM', '4');
 94 |     expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 2');
 95 | 
 96 |     $input1.autotype('{{right}}');
 97 |     expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
 98 | 
 99 |     //meridian
100 |     $input1.autotype('{{up}}');
101 |     expect(tp1.getTime()).toBe('11:30 PM', '5');
102 |     expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
103 | 
104 |     $input1.autotype('{{down}}');
105 |     expect(tp1.getTime()).toBe('11:30 AM', '6');
106 |     expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
107 | 
108 |     $input1.autotype('{{left}}');
109 |     expect(tp1.highlightedUnit).toBe('minute', 'minutes should be highlighted');
110 | 
111 |     // minutes
112 |     $input1.autotype('{{down}}');
113 |     expect(tp1.getTime()).toBe('11:15 AM', '7');
114 | 
115 |     $input1.autotype('{{left}}');
116 |     expect(tp1.highlightedUnit).toBe('hour', 'hours should be highlighted');
117 | 
118 |     // hours
119 |     $input1.autotype('{{down}}');
120 |     expect(tp1.getTime()).toBe('10:15 AM', '8');
121 | 
122 |     $input1.autotype('{{left}}');
123 |     expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
124 | 
125 |     // meridian
126 |     $input1.autotype('{{down}}');
127 |     expect(tp1.getTime()).toBe('10:15 PM', '9');
128 |   });
129 | 
130 |   it('should move the cursor left when shift+tab is pressed', function() {
131 |     $input1.trigger('focus');
132 |     $input1.autotype('{{tab}}{{right}}');
133 |     expect(tp1.highlightedUnit).toBe('meridian');
134 | 
135 |     $input1.autotype('{{shift}}{{tab}}{{tab}}{{/shift}}');
136 |     expect(tp1.highlightedUnit).toBe('hour');
137 | 
138 |     $input1.autotype('{{shift}}{{tab}}{{/shift}}');
139 |     expect($input1.is(':focus')).toBe(false);
140 |   });
141 | 
142 |   it('should be able to control element with arrow keys and tab', function() {
143 |     tp5.setTime('12:00 AM');
144 |     tp5.update();
145 |     $input5.trigger('focus');
146 |     $input5.autotype('{{down}}{{tab}}{{up}}{{tab}}');
147 |     expect(tp5.getTime()).toBe('11:15 PM');
148 |     expect(tp5.highlightedUnit).toBe('meridian', 'tab should have highlighed meridian');
149 | 
150 |     $input5.autotype('{{shift}}{{tab}}{{/shift}}{{up}}');
151 |     expect(tp5.getTime()).toBe('11:30 PM', 'shift+tab up should have incremented minutes to next step');
152 | 
153 |     $input5.autotype('{{shift}}{{tab}}{{tab}}{{/shift}}');
154 |     expect($input5.is(':focus')).toBe(false, 'timepicker should not be focused');
155 |   });
156 | 
157 |   it('should be able to change time via widget inputs in a dropdown', function() {
158 |     var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
159 |     $minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
160 |     $meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
161 |     eventCount = 0,
162 |     time;
163 | 
164 |     tp1.setTime('9:30 AM');
165 |     $input1.parents('div').find('.input-group-addon').click();
166 | 
167 |     $input1.timepicker().on('changeTime.timepicker', function(e) {
168 |       eventCount++;
169 |       time = e.time.value;
170 |     });
171 | 
172 |     expect(tp1.isOpen).toBe(true, 'dropdown should be open');
173 | 
174 |     expect(tp1.getTime()).toBe('9:30 AM', 'should be default time');
175 | 
176 |     $hourInput.trigger('focus');
177 |     expect(eventCount).toBe(0, 'event count should be 0');
178 |     $hourInput.autotype('{{back}}{{back}}11{{tab}}');
179 | 
180 |     expect(tp1.getTime()).toBe('11:30 AM');
181 |     expect(eventCount).toBe(4, 'incorrect update events thrown');
182 |     expect(time).toBe('11:30 AM', 'event throwing wrong time');
183 | 
184 |     $minuteInput.autotype('{{back}}{{back}}45{{tab}}');
185 | 
186 |     expect(tp1.minute).toBe(45);
187 |     expect(eventCount).toBe(8, 'incorrect update events thrown');
188 |     expect(time).toBe('11:45 AM');
189 | 
190 |     $meridianInput.autotype('{{back}}{{back}}pm{{tab}}');
191 | 
192 |     expect(tp1.meridian).toBe('PM');
193 |     expect(eventCount).toBe(12, 'incorrect update events thrown');
194 |     expect(time).toBe('11:45 PM');
195 |   });
196 | 
197 |   it('should still be empty if input is empty', function() {
198 |     $input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{tab}}');
199 | 
200 |     expect($input1.val()).toBe('');
201 |   });
202 | 
203 |   it('should allow time to be changed via widget inputs in a modal', function() {
204 |     tp2.setTime('9:30 AM');
205 |     tp2.update();
206 |     $input2.parents('div').find('.input-group-addon').click();
207 | 
208 |     var $hourInput = $('body').find('input.bootstrap-timepicker-hour'),
209 |         $minuteInput = $('body').find('input.bootstrap-timepicker-minute'),
210 |         $secondInput = $('body').find('input.bootstrap-timepicker-second'),
211 |         $meridianInput = $('body').find('input.bootstrap-timepicker-meridian');
212 | 
213 |     $hourInput.autotype('{{back}}{{back}}2{{tab}}');
214 |     expect(tp2.getTime()).toBe('2:30:00 AM');
215 | 
216 |     $minuteInput.autotype('{{back}}{{back}}0{{tab}}');
217 |     expect(tp2.getTime()).toBe('2:00:00 AM');
218 | 
219 |     $secondInput.autotype('{{back}}{{back}}30{{tab}}');
220 |     expect(tp2.getTime()).toBe('2:00:30 AM');
221 | 
222 |     $meridianInput.autotype('{{back}}{{back}}p{{tab}}');
223 |     expect(tp2.getTime()).toBe('2:00:30 PM');
224 |   });
225 | 
226 |   it('should be 12:00 AM if 00:00 AM is entered', function() {
227 |     $input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}0:0 AM{{tab}}');
228 | 
229 |     expect(tp1.getTime()).toBe('12:00 AM');
230 |   });
231 | 
232 |   it('should snap to nearest step or overflow to zero if snapToStep is true', function() {
233 |     // TODO
234 |     // $input5.trigger('focus');
235 |     // expect(tp5.highlightedUnit).toBe('hour');
236 |     // $input5.autotype('12:43p{{tab}}{{tab}}{{down}}');
237 |     // expect(tp5.getTime()).toBe('12:45 PM');
238 |   });
239 | 
240 |   it('should validate input', function() {
241 |     var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
242 |     $minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
243 |     $meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
244 |     $input3 = tp3.$element;
245 | 
246 |     tp1.setTime('11:30 AM');
247 |     tp1.update();
248 | 
249 |     $hourInput.autotype('{{back}}{{back}}10{{tab}}');
250 |     expect(tp1.getTime()).toBe('10:30 AM');
251 | 
252 |     $minuteInput.autotype('{{back}}{{back}}60{{tab}}');
253 |     expect(tp1.getTime()).toBe('10:59 AM');
254 | 
255 |     $meridianInput.autotype('{{back}}{{back}}dk{{tab}}');
256 |     expect(tp1.getTime()).toBe('10:59 AM');
257 | 
258 |     $meridianInput.autotype('{{back}}{{back}}p{{tab}}');
259 |     expect(tp1.getTime()).toBe('10:59 PM');
260 | 
261 |     $input3.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}25:60:60{{tab}}');
262 |     expect(tp3.getTime()).toBe('23:59:59');
263 |   });
264 | 
265 |   it('should close timepicker widget on TAB out of field', function() {
266 |     $input1.trigger('focus');
267 |     tp1.showWidget();
268 |     expect(tp1.isOpen).toBe(true);
269 |     expect(tp1.highlightedUnit).toBe('hour');
270 | 
271 |     $input1.autotype('{{tab}}');
272 |     expect(tp1.highlightedUnit).toBe('minute');
273 | 
274 |     $input1.autotype('{{tab}}');
275 |     expect(tp1.highlightedUnit).toBe('meridian');
276 | 
277 |     $input1.autotype('{{tab}}');
278 |     expect(tp1.isOpen).toBe(false);
279 |   });
280 | 
281 |   it('should close timepicker widget on SHIFT+TAB out of field', function() {
282 |     $input1.trigger('focus');
283 |     tp1.showWidget();
284 |     expect(tp1.isOpen).toBe(true);
285 |     expect(tp1.highlightedUnit).toBe('hour');
286 | 
287 |     $input1.autotype('{{tab}}');
288 |     expect(tp1.highlightedUnit).toBe('minute');
289 | 
290 |     $input1.autotype('{{tab}}');
291 |     expect(tp1.highlightedUnit).toBe('meridian');
292 | 
293 |     $input1.autotype('{{shift}}{{tab}}{{/shift}}');
294 |     expect(tp1.highlightedUnit).toBe('minute');
295 | 
296 |     $input1.autotype('{{shift}}{{tab}}{{/shift}}');
297 |     expect(tp1.highlightedUnit).toBe('hour');
298 | 
299 |     $input1.autotype('{{shift}}{{tab}}{{/shift}}');
300 |     expect(tp1.isOpen).toBe(false);
301 |   });
302 | });
303 | 


--------------------------------------------------------------------------------
/spec/js/MouseEventsSpec.js:
--------------------------------------------------------------------------------
  1 | describe('Mouse events feature', function() {
  2 |   'use strict';
  3 | 
  4 |   var $input1,
  5 |     $input2,
  6 |     $input3,
  7 |     $input4,
  8 |     $input5,
  9 |     $timepicker1,
 10 |     $timepicker2,
 11 |     $timepicker3,
 12 |     $timepicker4,
 13 |     $timepicker5,
 14 |     tp1,
 15 |     tp2,
 16 |     tp3,
 17 |     tp4,
 18 |     tp5;
 19 | 
 20 |   beforeEach(function () {
 21 |     loadFixtures('timepicker.html');
 22 | 
 23 |     $input1 = $('#timepicker1');
 24 |     $timepicker1 = $input1.timepicker();
 25 |     tp1 = $timepicker1.data('timepicker');
 26 | 
 27 |     $input2 = $('#timepicker2');
 28 |     $timepicker2 = $input2.timepicker({
 29 |       template: 'modal',
 30 |       showSeconds: true,
 31 |       minuteStep: 30,
 32 |       secondStep: 30,
 33 |       defaultTime: false
 34 |     });
 35 |     tp2 = $timepicker2.data('timepicker');
 36 | 
 37 |     $input3 = $('#timepicker3');
 38 |     $timepicker3 = $input3.timepicker({
 39 |       defaultTime: '23:15:20',
 40 |       showMeridian: false,
 41 |       showSeconds: true
 42 |     });
 43 |     tp3 = $timepicker3.data('timepicker');
 44 | 
 45 |     $input4 = $('#timepicker4');
 46 |     $timepicker4 = $input4.timepicker({
 47 | 			minuteStep: 5,
 48 | 			showInputs: false,
 49 | 			showMeridian: true,
 50 | 			template: 'modal',
 51 | 			disableFocus: true
 52 |     });
 53 |     tp4 = $timepicker4.data('timepicker');
 54 | 
 55 |     $input5 = $('#timepicker5');
 56 |     $timepicker5 = $input5.timepicker({
 57 |       showWidgetOnAddonClick: false
 58 |     });
 59 |     tp5 = $timepicker5.data('timepicker');
 60 |   });
 61 | 
 62 |   afterEach(function () {
 63 |     $input1.data('timepicker').remove();
 64 |     $input2.data('timepicker').remove();
 65 |     $input3.data('timepicker').remove();
 66 |     $input4.data('timepicker').remove();
 67 |     $input5.data('timepicker').remove();
 68 |     $input1.remove();
 69 |     $input2.remove();
 70 |     $input3.remove();
 71 |     $input4.remove();
 72 |     $input5.remove();
 73 |   });
 74 | 
 75 |   it('should be shown and trigger show events on input click', function() {
 76 |     var showEvents = 0;
 77 | 
 78 |     $input1.on('show.timepicker', function() {
 79 |       showEvents++;
 80 |     });
 81 | 
 82 |     $input1.parents('div').find('.input-group-addon').trigger('click');
 83 | 
 84 |     expect(tp1.isOpen).toBe(true);
 85 |     expect(showEvents).toBe(1);
 86 |   });
 87 | 
 88 |   it('should be hidden and trigger hide events on click outside of widget', function() {
 89 |     var hideEvents = 0,
 90 |         time;
 91 |     tp1.setTime('11:30 AM');
 92 |     tp1.update();
 93 | 
 94 |     $input1.on('hide.timepicker', function(e) {
 95 |       hideEvents++;
 96 | 
 97 |       time = e.time.value;
 98 |     });
 99 | 
100 |     $input1.parents('div').find('.input-group-addon').trigger('click');
101 |     expect(tp1.isOpen).toBe(true);
102 | 
103 |     $('body').trigger('mousedown');
104 | 
105 |     expect(tp1.isOpen).toBe(false, 'widget is still open');
106 |     expect(hideEvents).toBe(1, 'hide event was not thrown once');
107 |     expect(time).toBe('11:30 AM');
108 | 
109 |   });
110 | 
111 |   it('should not show widget when clicking input-group-addon icon if showWidgetOnAddonClick is false', function() {
112 |     expect(tp5.isOpen).toBe(false);
113 |     $input5.parents('div').find('.input-group-addon').trigger('click');
114 |     expect(tp5.isOpen).toBe(false);
115 |   });
116 | 
117 |   it('should increment hour on button click', function() {
118 |     tp1.setTime('11:30 AM');
119 |     tp1.update();
120 | 
121 |     var count = 0;
122 |     $input1.on('changeTime.timepicker', function() {
123 |       count++;
124 |     });
125 | 
126 |     tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
127 | 
128 |     expect(tp1.getTime()).toBe('12:30 PM');
129 |     expect(count).toBe(1);
130 | 
131 |     tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
132 |     expect(tp2.getTime()).toBe('1:00:00 AM');
133 |   });
134 | 
135 |   it('should decrement hour on button click and fire 1 changeTime event', function() {
136 |     tp1.setTime('12:30 PM');
137 |     tp1.update();
138 | 
139 |     var count = 0;
140 |     $input1.on('changeTime.timepicker', function() {
141 |       count++;
142 |     });
143 | 
144 |     tp1.$widget.find('a[data-action="decrementHour"]').trigger('click');
145 | 
146 |     expect(tp1.getTime()).toBe('11:30 AM', 'meridian isnt toggling');
147 |     expect(count).toBe(1);
148 | 
149 |     tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
150 |     tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
151 |     tp2.$widget.find('a[data-action="decrementHour"]').trigger('click');
152 |     expect(tp2.getTime()).toBe('1:00:00 AM');
153 |   });
154 | 
155 |   it('should increment minute on button click and fire 1 changeTime event', function() {
156 |     tp1.setTime('11:30 AM');
157 |     tp1.update();
158 | 		tp4.setTime('11:30 AM');
159 | 		tp4.update();
160 | 
161 |     var count = 0;
162 |     $input1.on('changeTime.timepicker', function() {
163 |       count++;
164 |     });
165 | 
166 |     tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
167 |     expect(tp1.getTime()).toBe('11:45 AM');
168 | 
169 |     tp2.$widget.find('a[data-action="incrementMinute"]').trigger('click');
170 |     expect(tp2.getTime()).toBe('0:30:00 AM');
171 | 
172 |     expect(count).toBe(1);
173 | 
174 | 		$input4.trigger('click');
175 |     tp4.$widget.find('a[data-action="incrementMinute"]').trigger('click');
176 |     tp4.$widget.find('a[data-action="decrementHour"]').trigger('click');
177 | 		$input4.closest('modal').find('.btn-primary').trigger('click');
178 |     expect(tp4.getTime()).toBe('10:35 AM');
179 | 		expect($input4.val()).toBe('10:35 AM');
180 |   });
181 | 
182 |   it('should decrement minute on button click', function() {
183 |     tp1.setTime('12:30 PM');
184 |     tp1.update();
185 |     tp4.setTime('11:30 AM');
186 |     tp4.update();
187 | 
188 |     tp1.$widget.find('a[data-action="decrementMinute"]').trigger('click');
189 |     expect(tp1.getTime()).toBe('12:15 PM');
190 | 
191 |     tp4.$widget.find('a[data-action="decrementMinute"]').trigger('click');
192 |     expect(tp4.getTime()).toBe('11:25 AM');
193 |   });
194 | 
195 |   it('should go from 11:00 AM to 1:00 AM on 2 hour increments and fire 2 change time events', function() {
196 |     tp1.setTime('11:00 AM');
197 |     tp1.update();
198 | 
199 |     var count = 0;
200 |     $input1.on('changeTime.timepicker', function() {
201 |       count++;
202 |     });
203 | 
204 |     tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
205 |     tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
206 | 
207 |     expect(tp1.getTime()).toBe('1:00 PM');
208 | 
209 |     expect(count).toBe(2);
210 |   });
211 | 
212 |   it('should go from 11:45 AM to 12:00 PM on 4 minute increments and fire 1 change time events', function() {
213 |     tp1.setTime('11:45 AM');
214 |     tp1.update();
215 | 
216 |     var count = 0;
217 |     $input1.on('changeTime.timepicker', function() {
218 |       count++;
219 |     });
220 | 
221 |     tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
222 | 
223 |     expect(tp1.getTime()).toBe('12:00 PM');
224 | 
225 |     expect(count).toBe(1);
226 |   });
227 | 
228 | 
229 |   it('should be 11:30:00 PM if minute is decremented on empty input', function() {
230 |     tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
231 |     expect(tp2.getTime()).toBe('11:30:00 PM');
232 |   });
233 | 
234 |   it('should increment second on button click', function() {
235 |     tp2.setTime('11:30:15 AM');
236 |     tp2.update();
237 | 
238 |     tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
239 | 
240 |     expect(tp2.getTime()).toBe('11:30:30 AM');
241 |   });
242 | 
243 |   it('should decrement second on button click', function() {
244 |     tp2.setTime('12:30:15 PM');
245 |     tp2.update();
246 | 
247 |     tp2.$widget.find('a[data-action="decrementSecond"]').trigger('click');
248 | 
249 |     expect(tp2.getTime()).toBe('12:29:45 PM');
250 |   });
251 | 
252 |   it('should be 11:30:00 PM if minute is decremented on empty input', function() {
253 |     tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
254 |     expect(tp2.getTime()).toBe('11:30:00 PM');
255 |   });
256 | 
257 |   it('should increment second on button click', function() {
258 |     tp2.setTime('11:30:15 AM');
259 |     tp2.update();
260 | 
261 |     tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
262 | 
263 |     expect(tp2.getTime()).toBe('11:30:30 AM');
264 |   });
265 | 
266 |   it('should decrement second on button click', function() {
267 |     tp2.setTime('12:30:15 PM');
268 |     tp2.update();
269 | 
270 |     tp2.$widget.find('a[data-action="decrementSecond"]').trigger('click');
271 | 
272 |     expect(tp2.getTime()).toBe('12:29:45 PM');
273 |   });
274 | 
275 |   it('should toggle meridian on button click', function() {
276 |     tp1.setTime('12:30 PM');
277 |     tp1.update();
278 | 
279 |     tp1.$widget.find('a[data-action="toggleMeridian"]').first().trigger('click');
280 |     expect(tp1.getTime()).toBe('12:30 AM');
281 |     tp1.$widget.find('a[data-action="toggleMeridian"]').last().trigger('click');
282 |     expect(tp1.getTime()).toBe('12:30 PM');
283 |   });
284 | 
285 | 
286 |   it('should trigger changeTime event if time is changed', function() {
287 |     var eventCount = 0,
288 |         time;
289 | 
290 |     $input1.timepicker().on('changeTime.timepicker', function(e) {
291 |       eventCount++;
292 |       time = e.time.value;
293 |     });
294 | 
295 |     tp1.setTime('11:30 AM');
296 | 
297 |     expect(eventCount).toBe(1);
298 |     expect(time).toBe('11:30 AM');
299 | 
300 |     tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
301 | 
302 |     expect(eventCount).toBe(2);
303 |     expect(tp1.getTime()).toBe('12:30 PM');
304 |     expect(time).toBe('12:30 PM');
305 | 
306 |     tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
307 | 
308 |     expect(eventCount).toBe(3);
309 |     expect(tp1.getTime()).toBe('12:45 PM');
310 |   });
311 | 
312 |   it('should close all open widgets on document mousedown or touchend', function() {
313 |     tp1.showWidget();
314 |     tp2.showWidget();
315 |     expect(tp1.isOpen).toBe(true);
316 |     expect(tp2.isOpen).toBe(true);
317 |     $(document).trigger('mousedown');
318 |     expect(tp1.isOpen).toBe(false);
319 |     expect(tp2.isOpen).toBe(false);
320 | 
321 |     tp1.showWidget();
322 |     tp2.showWidget();
323 |     expect(tp1.isOpen).toBe(true);
324 |     expect(tp2.isOpen).toBe(true);
325 |     $(document).trigger('touchend');
326 |     expect(tp1.isOpen).toBe(false);
327 |     expect(tp2.isOpen).toBe(false);
328 |   });
329 | 
330 |   it('should highlight widget inputs on click', function() {
331 |     tp1.setTime('11:55 AM');
332 |     tp1.update();
333 | 
334 |     $input1.parents('.bootstrap-timepicker').find('.input-group-addon').trigger('click');
335 |     expect(tp1.isOpen).toBe(true);
336 |     expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('11');
337 |     tp1.$widget.find('.bootstrap-timepicker-hour').trigger('click');
338 |     var hour1 = window.getSelection().toString();
339 | 
340 |     expect(hour1).toBe('11', 'hour input not being highlighted');
341 | 
342 |     tp1.$widget.find('.bootstrap-timepicker-minute').trigger('click');
343 |     var minute1 = window.getSelection().toString();
344 |     expect(minute1).toBe('55', 'minute input not being highlighted');
345 | 
346 |     tp1.$widget.find('.bootstrap-timepicker-meridian').trigger('click');
347 |     var meridian1 = window.getSelection().toString();
348 |     expect(meridian1).toBe('AM', 'meridian input not being highlighted');
349 |   });
350 | });
351 | 


--------------------------------------------------------------------------------
/spec/js/TimepickerSpec.js:
--------------------------------------------------------------------------------
  1 | describe('Timepicker feature', function() {
  2 |   'use strict';
  3 | 
  4 |   var $input1,
  5 |     $input2,
  6 |     $input3,
  7 |     $input4,
  8 |     $input5,
  9 |     $timepicker1,
 10 |     $timepicker2,
 11 |     $timepicker3,
 12 |     $timepicker4,
 13 |     $timepicker5,
 14 |     tp1,
 15 |     tp2,
 16 |     tp3,
 17 |     tp4,
 18 |     tp5;
 19 | 
 20 |   beforeEach(function () {
 21 |     loadFixtures('timepicker.html');
 22 | 
 23 |     $input1 = $('#timepicker1');
 24 |     $timepicker1 = $input1.timepicker();
 25 |     tp1 = $timepicker1.data('timepicker');
 26 | 
 27 |     $input2 = $('#timepicker2');
 28 |     $timepicker2 = $input2.timepicker({
 29 |       template: 'modal',
 30 |       showSeconds: true,
 31 |       minuteStep: 30,
 32 |       secondStep: 30,
 33 |       defaultTime: false
 34 |     });
 35 |     tp2 = $timepicker2.data('timepicker');
 36 | 
 37 |     $input3 = $('#timepicker3');
 38 |     $timepicker3 = $input3.timepicker({
 39 |       showMeridian: false,
 40 |       showSeconds: true,
 41 |       defaultTime: '13:25:15',
 42 |       maxHours: 100
 43 |     });
 44 |     tp3 = $timepicker3.data('timepicker');
 45 | 
 46 |     $input4 = $('#timepicker-z-index');
 47 |     $timepicker4 = $input4.timepicker();
 48 |     tp4 = $timepicker4.data('timepicker');
 49 | 
 50 |     $input5 = $('#timepicker-snapper');
 51 |     $timepicker5 = $input5.timepicker({
 52 |       snapToStep: true,
 53 |       minuteStep: 30,
 54 |       defaultTime: '12:00 AM'
 55 |     });
 56 |     tp5 = $timepicker5.data('timepicker');
 57 |   });
 58 | 
 59 |   afterEach(function () {
 60 |     if ($input1.data('timepicker') !== undefined) {
 61 |       $input1.data('timepicker').remove();
 62 |     }
 63 |     if ($input2.data('timepicker') !== undefined) {
 64 |       $input2.data('timepicker').remove();
 65 |     }
 66 |     if ($input3.data('timepicker') !== undefined) {
 67 |       $input3.data('timepicker').remove();
 68 |     }
 69 |     if ($input4.data('timepicker') !== undefined) {
 70 |       $input4.data('timepicker').remove();
 71 |     }
 72 |     if ($input5.data('timepicker') !== undefined) {
 73 |       $input5.data('timepicker').remove();
 74 |     }
 75 |     $input1.remove();
 76 |     $input2.remove();
 77 |     $input3.remove();
 78 |     $input4.remove();
 79 |     $input5.remove();
 80 |   });
 81 | 
 82 |   it('should be available on the jquery object', function() {
 83 |     expect($.fn.timepicker).toBeDefined();
 84 |   });
 85 | 
 86 |   it('should be chainable', function() {
 87 |     expect($timepicker1).toBe($input1);
 88 |   });
 89 | 
 90 |   it('should have sensible defaults', function() {
 91 |     expect(tp1.defaultTime).toBeTruthy();
 92 |     expect(tp1.minuteStep).toBe(15);
 93 |     expect(tp1.secondStep).toBe(15);
 94 |     expect(tp1.snapToStep).toBe(false);
 95 |     expect(tp1.disableFocus).toBe(false);
 96 |     expect(tp1.showSeconds).toBe(false);
 97 |     expect(tp1.showInputs).toBe(true);
 98 |     expect(tp1.showMeridian).toBe(true);
 99 |     expect(tp1.template).toBe('dropdown');
100 |     expect(tp1.appendWidgetTo).toBe('body');
101 |     expect(tp1.modalBackdrop).toBe(false);
102 |     expect(tp1.modalBackdrop).toBe(false);
103 |     expect(tp1.isOpen).toBe(false);
104 |     expect(tp1.showWidgetOnAddonClick).toBe(true);
105 |     expect(tp1.maxHours).toBe(24);
106 |   });
107 | 
108 |   it('should allow user to configure defaults', function() {
109 |     expect(tp2.template).toBe('modal');
110 |     expect(tp2.minuteStep).toBe(30);
111 |   });
112 | 
113 |   it('should be configurable with data attributes', function() {
114 |     $('body').append('<div id="hi" class="bootstrap-timepicker"><input id="customTimepicker" data-template="modal" data-minute-step="30" data-modal-backdrop="true" data-show-meridian="true" type="text"/></div');
115 | 
116 |     var $customInput = $('body').find('#customTimepicker'),
117 |         tpCustom = $customInput.timepicker().data('timepicker');
118 | 
119 |     expect($('body').find('#customTimepicker').length).toBe(1);
120 |     expect(tpCustom.template).toBe('modal');
121 |     expect(tpCustom.minuteStep).toBe(30, 'data-minute-step not working');
122 |     expect(tpCustom.modalBackdrop).toBe(true, 'data-modal-backdrop not working');
123 |     expect(tpCustom.showMeridian).toBe(true, 'data-show-meridian not working');
124 | 
125 |     tpCustom.remove();
126 |   });
127 | 
128 |   it('should be initialized on click with data-provide attribute', function() {
129 |     $('body').append('<div id="foo" class="bootstrap-timepicker"><input id="fooTimepicker" data-provide="timepicker" type="text" /></div>');
130 |     $('#fooTimepicker').trigger('click');
131 | 
132 |     expect($('#fooTimepicker').data('timepicker')).toBeDefined();
133 |   });
134 | 
135 |   it('should be initialized on focus with data-provide attribute', function() {
136 |     $('body').append('<div id="bar" class="bootstrap-timepicker"><input id="barTimepicker" data-provide="timepicker" type="text" /></div>');
137 |     $('#barTimepicker').trigger('focus');
138 | 
139 |     expect($('#barTimepicker').data('timepicker')).toBeDefined();
140 |   });
141 | 
142 |   it('should have current time by default', function() {
143 |     var dTime = new Date(),
144 |       hour = dTime.getHours(),
145 |       minutes = dTime.getMinutes(),
146 |       meridian;
147 | 
148 |     if (minutes !== 0) {
149 |       minutes = Math.ceil(minutes / tp1.minuteStep) * tp1.minuteStep;
150 |     }
151 | 
152 |     if (minutes === 60) {
153 |       hour += 1;
154 |       minutes = 0;
155 |     }
156 | 
157 |     if (hour < 12) {
158 |       meridian = 'AM';
159 |     } else {
160 |       meridian = 'PM';
161 |     }
162 | 
163 |     if (hour > 12) {
164 |       hour = hour - 12;
165 |     }
166 |     if (hour === 0) {
167 |       hour = 12;
168 |     }
169 | 
170 |     expect(tp1.hour).toBe(hour);
171 |     expect(tp1.minute).toBe(minutes);
172 |     expect(tp1.meridian).toBe(meridian);
173 |   });
174 | 
175 |   it('should not override time with current time if value is already set', function() {
176 |     $('body').append('<div id="timepickerCustom"><input id="timepickerCustomInput" type="text" value="12:15 AM" /></div>');
177 |     var $customInput = $('#timepickerCustomInput').timepicker(),
178 |       tpCustom = $customInput.data('timepicker');
179 | 
180 |     expect($customInput.val()).toBe('12:15 AM');
181 | 
182 |     tpCustom.remove();
183 |     $('#timepickerCustom').remove();
184 |   });
185 | 
186 |   it('should have no value if defaultTime is set to false', function() {
187 |     expect($input2.val()).toBe('');
188 |   });
189 | 
190 |   it('should be able to set default time with config option', function() {
191 |     expect(tp3.getTime()).toBe('13:25:15');
192 |   });
193 | 
194 |   it('should update the element and widget with the setTime method', function() {
195 |     tp2.setTime('9:15:20 AM');
196 | 
197 |     expect(tp2.hour).toBe(9);
198 |     expect(tp2.minute).toBe(15);
199 |     expect(tp2.second).toBe(20);
200 |     expect(tp2.meridian).toBe('AM');
201 |     expect($input2.val()).toBe('9:15:20 AM');
202 |     expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('9');
203 |     expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('15');
204 |     expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('20');
205 |     expect(tp2.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
206 |   });
207 | 
208 |   it('should be able get & set the pickers time', function() {
209 |     tp1.setTime('11:15 PM');
210 |     expect(tp1.getTime()).toBe('11:15 PM');
211 |     tp3.setTime('23:15:20');
212 |     expect(tp3.getTime()).toBe('23:15:20');
213 |     tp3.setTime('60:00:00');
214 |     expect(tp3.getTime()).toBe('60:00:00');
215 | 
216 |     tp1.setTime('11pm');
217 |     expect(tp1.getTime()).toBe('11:00 PM');
218 |     tp3.setTime('11pm');
219 |     expect(tp3.getTime()).toBe('23:00:00');
220 | 
221 |     tp1.setTime('11a');
222 |     expect(tp1.getTime()).toBe('11:00 AM');
223 |     tp3.setTime('11a');
224 |     expect(tp3.getTime()).toBe('11:00:00');
225 | 
226 |     tp1.setTime('1');
227 |     expect(tp1.getTime()).toBe('1:00 AM');
228 |     tp3.setTime('1');
229 |     expect(tp3.getTime()).toBe('1:00:00');
230 | 
231 |     tp1.setTime('13');
232 |     expect(tp1.getTime()).toBe('1:00 PM');
233 |     tp3.setTime('13');
234 |     expect(tp3.getTime()).toBe('13:00:00');
235 | 
236 |     tp1.setTime('10:20p');
237 |     expect(tp1.getTime()).toBe('10:20 PM');
238 |     tp3.setTime('10:20p');
239 |     expect(tp3.getTime()).toBe('22:20:00');
240 | 
241 |     tp1.setTime('10:20 p.m.');
242 |     expect(tp1.getTime()).toBe('10:20 PM');
243 |     tp3.setTime('10:20 p.m.');
244 |     expect(tp3.getTime()).toBe('22:20:00');
245 | 
246 |     tp1.setTime('10:20a');
247 |     expect(tp1.getTime()).toBe('10:20 AM');
248 |     tp3.setTime('10:20a');
249 |     expect(tp3.getTime()).toBe('10:20:00');
250 | 
251 |     tp1.setTime('10:2010');
252 |     expect(tp1.getTime()).toBe('10:20 AM', 'setTime with 10:2010 on tp1');
253 |     tp3.setTime('10:2010');
254 |     expect(tp3.getTime()).toBe('10:20:10', 'setTime with 10:2010 on tp3');
255 | 
256 |     tp1.setTime('102010');
257 |     expect(tp1.getTime()).toBe('10:20 AM', 'setTime with 102010 on tp1');
258 |     tp3.setTime('102010');
259 |     expect(tp3.getTime()).toBe('10:20:10', 'setTime with 102010 on tp3');
260 | 
261 |     tp1.setTime('2320');
262 |     expect(tp1.getTime()).toBe('11:20 PM', 'setTime with 2320 on tp1');
263 |     tp3.setTime('2320');
264 |     expect(tp3.getTime()).toBe('23:20:00', 'setTime with 2320 on tp3');
265 | 
266 |     tp3.setTime('0:00');
267 |     expect(tp3.getTime()).toBe('0:00:00', 'setTime with 0:00 on tp3');
268 | 
269 |     tp3.setTime('12:00 AM');
270 |     expect(tp3.getTime()).toBe('0:00:00', 'setTime with 0:00 on tp3');
271 |   });
272 | 
273 |   it('should update picker on blur', function() {
274 |     $input1.val('10:25 AM');
275 |     expect(tp1.getTime()).not.toBe('10:25 AM');
276 |     $input1.trigger('blur');
277 |     expect(tp1.getTime()).toBe('10:25 AM');
278 |   });
279 | 
280 |   it('should update element with updateElement method', function() {
281 |     tp1.hour = 10;
282 |     tp1.minute = 30;
283 |     tp1.meridian = 'PM';
284 |     tp1.updateElement();
285 |     expect($input1.val()).toBe('10:30 PM');
286 |   });
287 | 
288 |   it('should update widget with updateWidget method', function() {
289 |     tp2.hour = 10;
290 |     tp2.minute = 30;
291 |     tp2.second = 15;
292 | 
293 |     expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
294 |     expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
295 |     expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).not.toBe('15');
296 | 
297 |     tp2.updateWidget();
298 | 
299 |     expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
300 |     expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
301 |     expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('15');
302 |   });
303 | 
304 |   it('should update picker with updateFromElementVal method', function() {
305 |     tp1.hour = 12;
306 |     tp1.minute = 12;
307 |     tp1.meridian = 'PM';
308 |     tp1.update();
309 | 
310 |     $input1.val('10:30 AM');
311 | 
312 |     expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
313 |     expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
314 |     expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).not.toBe('AM');
315 |     expect(tp1.hour).not.toBe(10);
316 |     expect(tp1.minute).not.toBe(30);
317 |     expect(tp1.meridian).not.toBe('AM');
318 | 
319 |     tp1.updateFromElementVal();
320 | 
321 |     expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
322 |     expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
323 |     expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
324 |     expect(tp1.hour).toBe(10);
325 |     expect(tp1.minute).toBe(30);
326 |     expect(tp1.meridian).toBe('AM');
327 |   });
328 | 
329 |   it('should update picker with updateFromWidgetInputs method', function() {
330 |     tp1.hour = 12;
331 |     tp1.minute = 12;
332 |     tp1.meridian = 'PM';
333 |     tp1.update();
334 | 
335 |     tp1.$widget.find('.bootstrap-timepicker-hour').val(10);
336 |     tp1.$widget.find('.bootstrap-timepicker-minute').val(30);
337 |     tp1.$widget.find('.bootstrap-timepicker-meridian').val('AM');
338 | 
339 |     expect(tp1.hour).not.toBe(10);
340 |     expect(tp1.minute).not.toBe(30);
341 |     expect(tp1.meridian).not.toBe('AM');
342 |     expect($input1.val()).not.toBe('10:30 AM');
343 | 
344 |     tp1.updateFromWidgetInputs();
345 | 
346 |     expect(tp1.hour).toBe(10);
347 |     expect(tp1.minute).toBe(30);
348 |     expect(tp1.meridian).toBe('AM');
349 |     expect($input1.val()).toBe('10:30 AM');
350 |   });
351 | 
352 |   it('should increment hours with incrementHour method', function() {
353 |     tp1.hour = 9;
354 |     tp1.incrementHour();
355 |     expect(tp1.hour).toBe(10);
356 |   });
357 | 
358 |   it('should decrement hours with decrementHour method', function() {
359 |     tp1.hour = 9;
360 |     tp1.decrementHour();
361 |     expect(tp1.hour).toBe(8);
362 |   });
363 | 
364 |   it('should toggle meridian if hour goes past 12', function() {
365 |     $input1.val('11:00 AM');
366 |     tp1.updateFromElementVal();
367 |     tp1.incrementHour();
368 | 
369 |     expect(tp1.hour).toBe(12);
370 |     expect(tp1.minute).toBe(0);
371 |     expect(tp1.meridian).toBe('PM');
372 |   });
373 | 
374 |   it('should toggle meridian if hour goes below 1', function() {
375 |     $input1.val('11:00 AM');
376 |     tp1.updateFromElementVal();
377 |     tp1.incrementHour();
378 | 
379 |     expect(tp1.hour).toBe(12);
380 |     expect(tp1.minute).toBe(0);
381 |     expect(tp1.meridian).toBe('PM');
382 |   });
383 | 
384 |   it('should set hour to 1 if hour increments on 12 for 12h clock', function() {
385 |     $input1.val('11:15 PM');
386 |     tp1.updateFromElementVal();
387 |     tp1.incrementHour();
388 |     tp1.incrementHour();
389 | 
390 |     expect(tp1.getTime()).toBe('1:15 AM');
391 |   });
392 | 
393 |   it('should set hour to 0 if hour increments on "maxHours-1" for 24h clock', function() {
394 |     $input3.val('98:15:30');
395 |     tp3.updateFromElementVal();
396 |     tp3.incrementHour();
397 |     tp3.incrementHour();
398 | 
399 |     expect(tp3.hour).toBe(0);
400 |     expect(tp3.minute).toBe(15);
401 |     expect(tp3.second).toBe(30);
402 |   });
403 | 
404 |   it('should increment minutes with incrementMinute method', function() {
405 |     tp1.minute = 10;
406 |     tp1.incrementMinute();
407 | 
408 |     expect(tp1.minute).toBe(15);
409 | 
410 |     tp2.minute = 0;
411 |     tp2.incrementMinute();
412 | 
413 |     expect(tp2.minute).toBe(30);
414 |   });
415 | 
416 |   it('should decrement minutes with decrementMinute method', function() {
417 |     tp1.hour = 11;
418 |     tp1.minute = 0;
419 |     tp1.decrementMinute();
420 | 
421 |     expect(tp1.hour).toBe(10);
422 |     expect(tp1.minute).toBe(45);
423 | 
424 |     tp2.hour = 11;
425 |     tp2.minute = 0;
426 |     tp2.decrementMinute();
427 | 
428 |     expect(tp2.hour).toBe(10);
429 |     expect(tp2.minute).toBe(30);
430 |   });
431 | 
432 |   it('should increment hour if minutes increment past 59', function() {
433 |     $input1.val('11:55 AM');
434 |     tp1.updateFromElementVal();
435 |     tp1.incrementMinute();
436 |     tp1.update();
437 | 
438 |     expect(tp1.getTime()).toBe('12:00 PM');
439 |   });
440 | 
441 |   it('should toggle meridian with toggleMeridian method', function() {
442 |     tp1.meridian = 'PM';
443 |     tp1.toggleMeridian();
444 | 
445 |     expect(tp1.meridian).toBe('AM');
446 |   });
447 | 
448 |   it('should increment seconds with incrementSecond method', function() {
449 |     tp1.second = 0;
450 |     tp1.incrementSecond();
451 | 
452 |     expect(tp1.second).toBe(15);
453 | 
454 |     tp2.second = 0;
455 |     tp2.incrementSecond();
456 | 
457 |     expect(tp2.second).toBe(30);
458 |   });
459 | 
460 |   it('should decrement seconds with decrementSecond method', function() {
461 |     tp2.hour = 11;
462 |     tp2.minute = 0;
463 |     tp2.second = 0;
464 |     tp2.decrementSecond();
465 | 
466 |     expect(tp2.minute).toBe(59);
467 |     expect(tp2.second).toBe(30);
468 |   });
469 | 
470 |   it('should increment minute by 1 if seconds increment past 59', function() {
471 |     $input2.val('11:55:30 AM');
472 |     tp2.updateFromElementVal();
473 |     tp2.incrementSecond();
474 |     tp2.update();
475 | 
476 |     expect(tp2.getTime()).toBe('11:56:00 AM');
477 |   });
478 | 
479 |   it('should not have any remaining events if remove is called', function() {
480 |     var hideEvents = 0;
481 | 
482 |     $input1.on('hide.timepicker', function() {
483 |       hideEvents++;
484 |     });
485 | 
486 |     $input1.parents('div').find('.input-group-addon').trigger('click');
487 |     $('body').trigger('mousedown');
488 | 
489 |     expect(hideEvents).toBe(1);
490 | 
491 |     tp1.remove();
492 |     tp2.remove();
493 |     tp3.remove();
494 |     tp4.remove();
495 | 
496 |     $('body').trigger('click');
497 |     expect(hideEvents).toBe(1);
498 |   });
499 | 
500 |   it('should be able to reset time by using setTime 0/null', function() {
501 |     tp1.hour = 10;
502 |     tp1.minute = 30;
503 |     tp1.meridian = 'PM';
504 |     tp1.updateElement();
505 | 
506 |     $input1.timepicker('setTime', null);
507 |     expect(tp1.getTime()).toBe('');
508 |   });
509 | 
510 |   it('should snap minutes to multiple of step when using setTime', function() {
511 |     tp5.setTime('2:33 AM');
512 |     expect(tp5.getTime()).toBe('2:30 AM');
513 |   });
514 | 
515 |   it('should place timepicker on top of parents', function() {
516 |     tp4.showWidget();
517 |     $('body').find('.bootstrap-timepicker-widget').css('position', 'relative');
518 |     expect($('body').find('.bootstrap-timepicker-widget').css('z-index')).toBe('1010');
519 |   });
520 | 
521 |   it('should not have the widget in the DOM if remove method is called', function() {
522 |     tp1.showWidget();
523 |     tp2.showWidget();
524 |     tp3.showWidget();
525 |     tp4.showWidget();
526 |     expect($('body')).toContain('.bootstrap-timepicker-widget');
527 |     tp1.remove();
528 |     tp2.remove();
529 |     tp3.remove();
530 |     tp4.remove();
531 |     expect($('body')).not.toContain('.bootstrap-timepicker-widget');
532 |   });
533 | 
534 |   it('should be able to set time from a script', function() {
535 |     $input1.timepicker('setTime', '12:35 PM');
536 |     tp1.update();
537 |     expect(tp1.getTime()).toBe('12:35 PM');
538 |   });
539 | 
540 |   it('should be able to opened from script', function() {
541 |     expect(tp1.isOpen).toBe(false);
542 |     $input1.timepicker('showWidget');
543 |     expect(tp1.isOpen).toBe(true);
544 |   });
545 | 
546 | });
547 | 


--------------------------------------------------------------------------------
/spec/js/fixtures/timepicker.html:
--------------------------------------------------------------------------------
 1 | <div class="bootstrap-timepicker input-group">
 2 |     <input id="timepicker1" type="text" class="form-control input-small">
 3 |     <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
 4 | </div>
 5 | <div class="input-group bootstrap-timepicker">
 6 |     <input id="timepicker2" type="text" class="form-control input-small">
 7 |     <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
 8 | </div>
 9 | <div class="bootstrap-timepicker">
10 |     <input id="timepicker3" type="text" class="form-control">
11 | </div>
12 | <div class="bootstrap-timepicker">
13 |     <input id="timepicker4" type="text" class="form-control">
14 | </div>
15 | <div class="input-group bootstrap-timepicker">
16 |     <input id="timepicker5" type="text" class="form-control input-small">
17 |     <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
18 | </div>
19 | <div style="z-index:1000; position:relative;">
20 |     <div class="bootstrap-timepicker">
21 |         <input id="timepicker-z-index" type="text" class="form-control">
22 |     </div>
23 | </div>
24 | <div class="input-group bootstrap-timepicker">
25 |     <input id="timepicker-snapper" type="text" class="form-control input-small">
26 |     <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
27 | </div>
28 | 


--------------------------------------------------------------------------------
/spec/js/helpers/SpecHelper.js:
--------------------------------------------------------------------------------
1 | 
2 | 


--------------------------------------------------------------------------------
/spec/js/helpers/jasmine-jquery.js:
--------------------------------------------------------------------------------
  1 | var readFixtures = function() {
  2 |   return jasmine.getFixtures().proxyCallTo_('read', arguments)
  3 | }
  4 | 
  5 | var preloadFixtures = function() {
  6 |   jasmine.getFixtures().proxyCallTo_('preload', arguments)
  7 | }
  8 | 
  9 | var loadFixtures = function() {
 10 |   jasmine.getFixtures().proxyCallTo_('load', arguments)
 11 | }
 12 | 
 13 | var appendLoadFixtures = function() {
 14 |   jasmine.getFixtures().proxyCallTo_('appendLoad', arguments)
 15 | }
 16 | 
 17 | var setFixtures = function(html) {
 18 |   jasmine.getFixtures().proxyCallTo_('set', arguments)
 19 | }
 20 | 
 21 | var appendSetFixtures = function() {
 22 |   jasmine.getFixtures().proxyCallTo_('appendSet', arguments)
 23 | }
 24 | 
 25 | var sandbox = function(attributes) {
 26 |   return jasmine.getFixtures().sandbox(attributes)
 27 | }
 28 | 
 29 | var spyOnEvent = function(selector, eventName) {
 30 |   return jasmine.JQuery.events.spyOn(selector, eventName)
 31 | }
 32 | 
 33 | var preloadStyleFixtures = function() {
 34 |   jasmine.getStyleFixtures().proxyCallTo_('preload', arguments)
 35 | }
 36 | 
 37 | var loadStyleFixtures = function() {
 38 |   jasmine.getStyleFixtures().proxyCallTo_('load', arguments)
 39 | }
 40 | 
 41 | var appendLoadStyleFixtures = function() {
 42 |   jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments)
 43 | }
 44 | 
 45 | var setStyleFixtures = function(html) {
 46 |   jasmine.getStyleFixtures().proxyCallTo_('set', arguments)
 47 | }
 48 | 
 49 | var appendSetStyleFixtures = function(html) {
 50 |   jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments)
 51 | }
 52 | 
 53 | var loadJSONFixtures = function() {
 54 |   return jasmine.getJSONFixtures().proxyCallTo_('load', arguments)
 55 | }
 56 | 
 57 | var getJSONFixture = function(url) {
 58 |   return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url]
 59 | }
 60 | 
 61 | jasmine.spiedEventsKey = function (selector, eventName) {
 62 |   return [$(selector).selector, eventName].toString()
 63 | }
 64 | 
 65 | jasmine.getFixtures = function() {
 66 |   return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures()
 67 | }
 68 | 
 69 | jasmine.getStyleFixtures = function() {
 70 |   return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures()
 71 | }
 72 | 
 73 | jasmine.Fixtures = function() {
 74 |   this.containerId = 'jasmine-fixtures'
 75 |   this.fixturesCache_ = {}
 76 |   this.fixturesPath = 'spec/js/fixtures'
 77 | }
 78 | 
 79 | jasmine.Fixtures.prototype.set = function(html) {
 80 |   this.cleanUp()
 81 |   this.createContainer_(html)
 82 | }
 83 | 
 84 | jasmine.Fixtures.prototype.appendSet= function(html) {
 85 |   this.addToContainer_(html)
 86 | }
 87 | 
 88 | jasmine.Fixtures.prototype.preload = function() {
 89 |   this.read.apply(this, arguments)
 90 | }
 91 | 
 92 | jasmine.Fixtures.prototype.load = function() {
 93 |   this.cleanUp()
 94 |   this.createContainer_(this.read.apply(this, arguments))
 95 | }
 96 | 
 97 | jasmine.Fixtures.prototype.appendLoad = function() {
 98 |   this.addToContainer_(this.read.apply(this, arguments))
 99 | }
100 | 
101 | jasmine.Fixtures.prototype.read = function() {
102 |   var htmlChunks = []
103 | 
104 |   var fixtureUrls = arguments
105 |   for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
106 |     htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex]))
107 |   }
108 | 
109 |   return htmlChunks.join('')
110 | }
111 | 
112 | jasmine.Fixtures.prototype.clearCache = function() {
113 |   this.fixturesCache_ = {}
114 | }
115 | 
116 | jasmine.Fixtures.prototype.cleanUp = function() {
117 |   $('#' + this.containerId).remove()
118 | }
119 | 
120 | jasmine.Fixtures.prototype.sandbox = function(attributes) {
121 |   var attributesToSet = attributes || {}
122 |   return $('<div id="sandbox" />').attr(attributesToSet)
123 | }
124 | 
125 | jasmine.Fixtures.prototype.createContainer_ = function(html) {
126 |   var container
127 |   if(html instanceof $) {
128 |     container = $('<div id="' + this.containerId + '" />')
129 |     container.html(html)
130 |   } else {
131 |     container = '<div id="' + this.containerId + '">' + html + '</div>'
132 |   }
133 |   $(document.body).append(container)
134 | }
135 | 
136 | jasmine.Fixtures.prototype.addToContainer_ = function(html){
137 |   var container = $(document.body).find('#'+this.containerId).append(html)
138 |   if(!container.length){
139 |     this.createContainer_(html)
140 |   }
141 | }
142 | 
143 | jasmine.Fixtures.prototype.getFixtureHtml_ = function(url) {
144 |   if (typeof this.fixturesCache_[url] === 'undefined') {
145 |     this.loadFixtureIntoCache_(url)
146 |   }
147 |   return this.fixturesCache_[url]
148 | }
149 | 
150 | jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
151 |   var url = this.makeFixtureUrl_(relativeUrl)
152 |   var request = $.ajax({
153 |     type: "GET",
154 |     url: url + "?" + new Date().getTime(),
155 |     async: false
156 |   })
157 |   this.fixturesCache_[relativeUrl] = request.responseText
158 | }
159 | 
160 | jasmine.Fixtures.prototype.makeFixtureUrl_ = function(relativeUrl){
161 |   return this.fixturesPath.match('/
#39;) ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
162 | }
163 | 
164 | jasmine.Fixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
165 |   return this[methodName].apply(this, passedArguments)
166 | }
167 | 
168 | 
169 | jasmine.StyleFixtures = function() {
170 |   this.fixturesCache_ = {}
171 |   this.fixturesNodes_ = []
172 |   this.fixturesPath = 'spec/javascripts/fixtures'
173 | }
174 | 
175 | jasmine.StyleFixtures.prototype.set = function(css) {
176 |   this.cleanUp()
177 |   this.createStyle_(css)
178 | }
179 | 
180 | jasmine.StyleFixtures.prototype.appendSet = function(css) {
181 |   this.createStyle_(css)
182 | }
183 | 
184 | jasmine.StyleFixtures.prototype.preload = function() {
185 |   this.read_.apply(this, arguments)
186 | }
187 | 
188 | jasmine.StyleFixtures.prototype.load = function() {
189 |   this.cleanUp()
190 |   this.createStyle_(this.read_.apply(this, arguments))
191 | }
192 | 
193 | jasmine.StyleFixtures.prototype.appendLoad = function() {
194 |   this.createStyle_(this.read_.apply(this, arguments))
195 | }
196 | 
197 | jasmine.StyleFixtures.prototype.cleanUp = function() {
198 |   while(this.fixturesNodes_.length) {
199 |     this.fixturesNodes_.pop().remove()
200 |   }
201 | }
202 | 
203 | jasmine.StyleFixtures.prototype.createStyle_ = function(html) {
204 |   var styleText = $('<div></div>').html(html).text(),
205 |     style = $('<style>' + styleText + '</style>')
206 | 
207 |   this.fixturesNodes_.push(style)
208 | 
209 |   $('head').append(style)
210 | }
211 | 
212 | jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache
213 | 
214 | jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read
215 | 
216 | jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_
217 | 
218 | jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_
219 | 
220 | jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_
221 | 
222 | jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_
223 | 
224 | jasmine.getJSONFixtures = function() {
225 |   return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures()
226 | }
227 | 
228 | jasmine.JSONFixtures = function() {
229 |   this.fixturesCache_ = {}
230 |   this.fixturesPath = 'spec/javascripts/fixtures/json'
231 | }
232 | 
233 | jasmine.JSONFixtures.prototype.load = function() {
234 |   this.read.apply(this, arguments)
235 |   return this.fixturesCache_
236 | }
237 | 
238 | jasmine.JSONFixtures.prototype.read = function() {
239 |   var fixtureUrls = arguments
240 |   for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
241 |     this.getFixtureData_(fixtureUrls[urlIndex])
242 |   }
243 |   return this.fixturesCache_
244 | }
245 | 
246 | jasmine.JSONFixtures.prototype.clearCache = function() {
247 |   this.fixturesCache_ = {}
248 | }
249 | 
250 | jasmine.JSONFixtures.prototype.getFixtureData_ = function(url) {
251 |   this.loadFixtureIntoCache_(url)
252 |   return this.fixturesCache_[url]
253 | }
254 | 
255 | jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
256 |   var self = this
257 |   var url = this.fixturesPath.match('/
#39;) ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
258 |   $.ajax({
259 |     async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
260 |     cache: false,
261 |     dataType: 'json',
262 |     url: url,
263 |     success: function(data) {
264 |       self.fixturesCache_[relativeUrl] = data
265 |     },
266 |     fail: function(jqXHR, status, errorThrown) {
267 |         throw Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')')
268 |     }
269 |   })
270 | }
271 | 
272 | jasmine.JSONFixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
273 |   return this[methodName].apply(this, passedArguments)
274 | }
275 | 
276 | jasmine.JQuery = function() {}
277 | 
278 | jasmine.JQuery.browserTagCaseIndependentHtml = function(html) {
279 |   return $('<div/>').append(html).html()
280 | }
281 | 
282 | jasmine.JQuery.elementToString = function(element) {
283 |   var domEl = $(element).get(0)
284 |   if (domEl == undefined || domEl.cloneNode)
285 |     return $('<div />').append($(element).clone()).html()
286 |   else
287 |     return element.toString()
288 | }
289 | 
290 | jasmine.JQuery.matchersClass = {}
291 | 
292 | !function(namespace) {
293 |   var data = {
294 |     spiedEvents: {},
295 |     handlers:    []
296 |   }
297 | 
298 |   namespace.events = {
299 |     spyOn: function(selector, eventName) {
300 |       var handler = function(e) {
301 |         data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = e
302 |       }
303 |       $(selector).bind(eventName, handler)
304 |       data.handlers.push(handler)
305 |       return {
306 |         selector: selector,
307 |         eventName: eventName,
308 |         handler: handler,
309 |         reset: function(){
310 |           delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
311 |         }
312 |       }
313 |     },
314 | 
315 |     wasTriggered: function(selector, eventName) {
316 |       return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)])
317 |     },
318 | 
319 |     wasPrevented: function(selector, eventName) {
320 |       var e;
321 |       return (e = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) && e.isDefaultPrevented()
322 |     },
323 | 
324 |     cleanUp: function() {
325 |       data.spiedEvents = {}
326 |       data.handlers    = []
327 |     }
328 |   }
329 | }(jasmine.JQuery)
330 | 
331 | !function(){
332 |   var jQueryMatchers = {
333 |     toHaveClass: function(className) {
334 |       return this.actual.hasClass(className)
335 |     },
336 | 
337 |     toHaveCss: function(css){
338 |       for (var prop in css){
339 |         if (this.actual.css(prop) !== css[prop]) return false
340 |       }
341 |       return true
342 |     },
343 | 
344 |     toBeVisible: function() {
345 |       return this.actual.is(':visible')
346 |     },
347 | 
348 |     toBeHidden: function() {
349 |       return this.actual.is(':hidden')
350 |     },
351 | 
352 |     toBeSelected: function() {
353 |       return this.actual.is(':selected')
354 |     },
355 | 
356 |     toBeChecked: function() {
357 |       return this.actual.is(':checked')
358 |     },
359 | 
360 |     toBeEmpty: function() {
361 |       return this.actual.is(':empty')
362 |     },
363 | 
364 |     toExist: function() {
365 |       return $(document).find(this.actual).length
366 |     },
367 | 
368 |     toHaveLength: function(length) {
369 |       return this.actual.length === length
370 |     },
371 | 
372 |     toHaveAttr: function(attributeName, expectedAttributeValue) {
373 |       return hasProperty(this.actual.attr(attributeName), expectedAttributeValue)
374 |     },
375 | 
376 |     toHaveProp: function(propertyName, expectedPropertyValue) {
377 |       return hasProperty(this.actual.prop(propertyName), expectedPropertyValue)
378 |     },
379 | 
380 |     toHaveId: function(id) {
381 |       return this.actual.attr('id') == id
382 |     },
383 | 
384 |     toHaveHtml: function(html) {
385 |       return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html)
386 |     },
387 | 
388 |     toContainHtml: function(html){
389 |       var actualHtml = this.actual.html()
390 |       var expectedHtml = jasmine.JQuery.browserTagCaseIndependentHtml(html)
391 |       return (actualHtml.indexOf(expectedHtml) >= 0)
392 |     },
393 | 
394 |     toHaveText: function(text) {
395 |       var trimmedText = $.trim(this.actual.text())
396 |       if (text && $.isFunction(text.test)) {
397 |         return text.test(trimmedText)
398 |       } else {
399 |         return trimmedText == text
400 |       }
401 |     },
402 | 
403 |     toHaveValue: function(value) {
404 |       return this.actual.val() == value
405 |     },
406 | 
407 |     toHaveData: function(key, expectedValue) {
408 |       return hasProperty(this.actual.data(key), expectedValue)
409 |     },
410 | 
411 |     toBe: function(selector) {
412 |       return this.actual.is(selector)
413 |     },
414 | 
415 |     toContain: function(selector) {
416 |       return this.actual.find(selector).length
417 |     },
418 | 
419 |     toBeDisabled: function(selector){
420 |       return this.actual.is(':disabled')
421 |     },
422 | 
423 |     toBeFocused: function(selector) {
424 |       return this.actual[0] === this.actual[0].ownerDocument.activeElement
425 |     },
426 | 
427 |     toHandle: function(event) {
428 | 
429 |       var events = $._data(this.actual.get(0), "events")
430 | 
431 |       if(!events || !event || typeof event !== "string") {
432 |         return false
433 |       }
434 | 
435 |       var namespaces = event.split(".")
436 |       var eventType = namespaces.shift()
437 |       var sortedNamespaces = namespaces.slice(0).sort()
438 |       var namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)")
439 | 
440 |       if(events[eventType] && namespaces.length) {
441 |         for(var i = 0; i < events[eventType].length; i++) {
442 |           var namespace = events[eventType][i].namespace
443 |           if(namespaceRegExp.test(namespace)) {
444 |             return true
445 |           }
446 |         }
447 |       } else {
448 |         return events[eventType] && events[eventType].length > 0
449 |       }
450 |     },
451 | 
452 |     // tests the existence of a specific event binding + handler
453 |     toHandleWith: function(eventName, eventHandler) {
454 |       var stack = $._data(this.actual.get(0), "events")[eventName]
455 |       for (var i = 0; i < stack.length; i++) {
456 |         if (stack[i].handler == eventHandler) return true
457 |       }
458 |       return false
459 |     }
460 |   }
461 | 
462 |   var hasProperty = function(actualValue, expectedValue) {
463 |     if (expectedValue === undefined) return actualValue !== undefined
464 |     return actualValue == expectedValue
465 |   }
466 | 
467 |   var bindMatcher = function(methodName) {
468 |     var builtInMatcher = jasmine.Matchers.prototype[methodName]
469 | 
470 |     jasmine.JQuery.matchersClass[methodName] = function() {
471 |       if (this.actual
472 |         && (this.actual instanceof $
473 |           || jasmine.isDomNode(this.actual))) {
474 |             this.actual = $(this.actual)
475 |             var result = jQueryMatchers[methodName].apply(this, arguments)
476 |             var element
477 |             if (this.actual.get && (element = this.actual.get()[0]) && !$.isWindow(element) && element.tagName !== "HTML")
478 |               this.actual = jasmine.JQuery.elementToString(this.actual)
479 |             return result
480 |           }
481 | 
482 |           if (builtInMatcher) {
483 |             return builtInMatcher.apply(this, arguments)
484 |           }
485 | 
486 |           return false
487 |     }
488 |   }
489 | 
490 |   for(var methodName in jQueryMatchers) {
491 |     bindMatcher(methodName)
492 |   }
493 | }()
494 | 
495 | beforeEach(function() {
496 |   this.addMatchers(jasmine.JQuery.matchersClass)
497 |   this.addMatchers({
498 |     toHaveBeenTriggeredOn: function(selector) {
499 |       this.message = function() {
500 |         return [
501 |           "Expected event " + this.actual + " to have been triggered on " + selector,
502 |           "Expected event " + this.actual + " not to have been triggered on " + selector
503 |         ]
504 |       }
505 |       return jasmine.JQuery.events.wasTriggered(selector, this.actual)
506 |     }
507 |   })
508 |   this.addMatchers({
509 |     toHaveBeenTriggered: function(){
510 |       var eventName = this.actual.eventName,
511 |           selector = this.actual.selector
512 |       this.message = function() {
513 |         return [
514 |           "Expected event " + eventName + " to have been triggered on " + selector,
515 |           "Expected event " + eventName + " not to have been triggered on " + selector
516 |         ]
517 |       }
518 |       return jasmine.JQuery.events.wasTriggered(selector, eventName)
519 |      }
520 |   })
521 |   this.addMatchers({
522 |     toHaveBeenPreventedOn: function(selector) {
523 |       this.message = function() {
524 |         return [
525 |           "Expected event " + this.actual + " to have been prevented on " + selector,
526 |           "Expected event " + this.actual + " not to have been prevented on " + selector
527 |         ]
528 |       }
529 |       return jasmine.JQuery.events.wasPrevented(selector, this.actual)
530 |     }
531 |   })
532 |   this.addMatchers({
533 |     toHaveBeenPrevented: function() {
534 |       var eventName = this.actual.eventName,
535 |           selector = this.actual.selector
536 |       this.message = function() {
537 |         return [
538 |           "Expected event " + eventName + " to have been prevented on " + selector,
539 |           "Expected event " + eventName + " not to have been prevented on " + selector
540 |         ]
541 |       }
542 |       return jasmine.JQuery.events.wasPrevented(selector, eventName)
543 |     }
544 |   })
545 | })
546 | 
547 | afterEach(function() {
548 |   jasmine.getFixtures().cleanUp()
549 |   jasmine.getStyleFixtures().cleanUp()
550 |   jasmine.JQuery.events.cleanUp()
551 | })
552 | 
553 | 


--------------------------------------------------------------------------------