Add frontend assets and plugin bundles
Add the legacy frontend themes, scripts, and plugin assets required by the main SPOTA interfaces.
This commit is contained in:
3
assets/plugins/bootstrap-timepicker/.bowerrc
Normal file
3
assets/plugins/bootstrap-timepicker/.bowerrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"directory" : "spec/js/libs/"
|
||||
}
|
||||
5
assets/plugins/bootstrap-timepicker/.gitignore
vendored
Normal file
5
assets/plugins/bootstrap-timepicker/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.swp
|
||||
_site
|
||||
node_modules
|
||||
_SpecRunner.html
|
||||
spec/js/libs
|
||||
3
assets/plugins/bootstrap-timepicker/.travis.yml
Normal file
3
assets/plugins/bootstrap-timepicker/.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
113
assets/plugins/bootstrap-timepicker/Gruntfile.js
Normal file
113
assets/plugins/bootstrap-timepicker/Gruntfile.js
Normal file
@@ -0,0 +1,113 @@
|
||||
module.exports = function(grunt) {
|
||||
'use strict';
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-jasmine');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
meta: {
|
||||
project: 'bootstrap-timepicker',
|
||||
version: '0.2.3'
|
||||
},
|
||||
jasmine: {
|
||||
build: {
|
||||
src : ['spec/js/libs/jquery/jquery.min.js', 'spec/js/libs/bootstrap/js/bootstrap.min.js', 'spec/js/libs/autotype/index.js', 'js/bootstrap-timepicker.js'],
|
||||
options: {
|
||||
specs : 'spec/js/*Spec.js',
|
||||
helpers : 'spec/js/helpers/*.js',
|
||||
timeout : 100
|
||||
}
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
browser: true,
|
||||
camelcase: true,
|
||||
curly: true,
|
||||
eqeqeq: true,
|
||||
eqnull: true,
|
||||
immed: true,
|
||||
indent: 2,
|
||||
latedef: true,
|
||||
newcap: true,
|
||||
noarg: true,
|
||||
quotmark: true,
|
||||
sub: true,
|
||||
strict: true,
|
||||
trailing: true,
|
||||
undef: true,
|
||||
unused: true,
|
||||
white: false,
|
||||
globals: {
|
||||
jQuery: true,
|
||||
$: true,
|
||||
expect: true,
|
||||
it: true,
|
||||
beforeEach: true,
|
||||
afterEach: true,
|
||||
describe: true,
|
||||
loadFixtures: true,
|
||||
console: true,
|
||||
module: true
|
||||
}
|
||||
},
|
||||
files: ['js/bootstrap-timepicker.js', 'Gruntfile.js', 'package.json', 'spec/js/*Spec.js']
|
||||
},
|
||||
less: {
|
||||
dev: {
|
||||
options: {
|
||||
paths: ['css']
|
||||
},
|
||||
files: {
|
||||
'css/bootstrap-timepicker.css': ['less/*.less']
|
||||
}
|
||||
},
|
||||
prod: {
|
||||
options: {
|
||||
paths: ['css'],
|
||||
yuicompress: true
|
||||
},
|
||||
files: {
|
||||
'css/bootstrap-timepicker.min.css': ['less/*.less']
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
banner: '/*! <%= meta.project %> v<%= meta.version %> \n' +
|
||||
'* http://jdewit.github.com/bootstrap-timepicker \n' +
|
||||
'* Copyright (c) <%= grunt.template.today("yyyy") %> Joris de Wit \n' +
|
||||
'* MIT License \n' +
|
||||
'*/'
|
||||
},
|
||||
build: {
|
||||
src: ['<banner:meta.banner>','js/<%= pkg.name %>.js'],
|
||||
dest: 'js/<%= pkg.name %>.min.js'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
js: {
|
||||
files: ['js/bootstrap-timepicker.js', 'spec/js/*Spec.js'],
|
||||
tasks: ['jshint', 'jasmine'],
|
||||
options: {
|
||||
livereload: true
|
||||
}
|
||||
},
|
||||
less: {
|
||||
files: ['less/timepicker.less'],
|
||||
tasks: ['less:dev'],
|
||||
options: {
|
||||
livereload: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['jshint', 'jasmine', 'less:dev']);
|
||||
grunt.registerTask('test', ['jasmine', 'jshint']);
|
||||
grunt.registerTask('compile', ['jshint', 'jasmine', 'uglify', 'less:prod']);
|
||||
};
|
||||
20
assets/plugins/bootstrap-timepicker/LICENSE
Normal file
20
assets/plugins/bootstrap-timepicker/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
54
assets/plugins/bootstrap-timepicker/README.md
Normal file
54
assets/plugins/bootstrap-timepicker/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
Timepicker for Twitter Bootstrap 2.x [](http://travis-ci.org/jdewit/bootstrap-timepicker)
|
||||
------------------------------------
|
||||
|
||||
A simple timepicker component for Twitter Bootstrap.
|
||||
|
||||
Demos & Documentation
|
||||
=====================
|
||||
|
||||
View <a href="http://jdewit.github.com/bootstrap-timepicker">demos & documentation</a>.
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
If you make money using this timepicker, please consider
|
||||
supporting its development.
|
||||
|
||||
<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>
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
1. Install <a href="www.nodejs.org">NodeJS</a> and <a href="www.npmjs.org">Node Package Manager</a>.
|
||||
|
||||
2. Install packages
|
||||
|
||||
``` bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. Use <a href="https://github.com/twitter/bower">Bower</a> to get the dev dependencies.
|
||||
|
||||
``` bash
|
||||
$ bower install
|
||||
```
|
||||
|
||||
4. Use <a href="www.gruntjs.com">Grunt</a> to run tests, compress assets, etc.
|
||||
|
||||
``` bash
|
||||
$ grunt test // run jshint and jasmine tests
|
||||
$ grunt watch // run jsHint and Jasmine tests whenever a file is changed
|
||||
$ grunt compile // minify the js and css files
|
||||
```
|
||||
|
||||
- Please make it easy on me by covering any new features or issues
|
||||
with <a href="http://pivotal.github.com/jasmine">Jasmine</a> tests.
|
||||
- If your changes need documentation, please take the time to update the docs.
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Thanks to everyone who have given feedback and submitted pull requests. A
|
||||
list of all the contributors can be found <a href="https://github.com/jdewit/bootstrap-timepicker/graphs/contributors">here</a>.
|
||||
|
||||
Special thanks to @eternicode and his <a href="https://github.com/eternicode/bootstrap-datepicker">Twitter Datepicker</a> for inspiration.
|
||||
16
assets/plugins/bootstrap-timepicker/component.json
Normal file
16
assets/plugins/bootstrap-timepicker/component.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "bootstrap-timepicker",
|
||||
"version": "0.2.3",
|
||||
"description": "A timepicker component for Twitter Bootstrap 2.x",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jdewit/bootstrap-timepicker"
|
||||
},
|
||||
"main": ["js/bootstrap-timepicker.min.js", "css/bootstrap-timepicker.min.css"],
|
||||
"dependencies": {
|
||||
"bootstrap": ">=2.0.4 <3.0",
|
||||
"jquery": ">=1.8.3",
|
||||
"autotype": "https://raw.github.com/mmonteleone/jquery.autotype/master/jquery.autotype.js"
|
||||
}
|
||||
}
|
||||
|
||||
13
assets/plugins/bootstrap-timepicker/composer.json
Normal file
13
assets/plugins/bootstrap-timepicker/composer.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name" : "jdewit/bootstrap-timepicker",
|
||||
"description" : "A simple timepicker component for Twitter Bootstrap.",
|
||||
"version" : "0.2.3",
|
||||
"license" : "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name" : "Joris de Wit",
|
||||
"email" : "joris.w.dewit@gmail.com"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
121
assets/plugins/bootstrap-timepicker/css/bootstrap-timepicker.css
vendored
Normal file
121
assets/plugins/bootstrap-timepicker/css/bootstrap-timepicker.css
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/*!
|
||||
* Timepicker Component for Twitter Bootstrap
|
||||
*
|
||||
* Copyright 2013 Joris de Wit
|
||||
*
|
||||
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
.bootstrap-timepicker {
|
||||
position: relative;
|
||||
}
|
||||
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before {
|
||||
left: auto;
|
||||
right: 12px;
|
||||
}
|
||||
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after {
|
||||
left: auto;
|
||||
right: 13px;
|
||||
}
|
||||
.bootstrap-timepicker .add-on {
|
||||
cursor: pointer;
|
||||
}
|
||||
.bootstrap-timepicker .add-on i {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.bootstrap-timepicker-widget.dropdown-menu {
|
||||
padding: 2px 3px 2px 2px;
|
||||
}
|
||||
.bootstrap-timepicker-widget.dropdown-menu.open {
|
||||
display: inline-block;
|
||||
}
|
||||
.bootstrap-timepicker-widget.dropdown-menu:before {
|
||||
border-bottom: 7px solid rgba(0, 0, 0, 0.2);
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
left: 9px;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
}
|
||||
.bootstrap-timepicker-widget.dropdown-menu:after {
|
||||
border-bottom: 6px solid #FFFFFF;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
}
|
||||
.bootstrap-timepicker-widget a.btn,
|
||||
.bootstrap-timepicker-widget input {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.bootstrap-timepicker-widget table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td {
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td:not(.separator) {
|
||||
min-width: 30px;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td span {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td a {
|
||||
border: 1px transparent solid;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 8px 0;
|
||||
outline: 0;
|
||||
color: #333;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td a:hover {
|
||||
text-decoration: none;
|
||||
background-color: #eee;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td a i {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.bootstrap-timepicker-widget table td input {
|
||||
width: 25px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.bootstrap-timepicker-widget .modal-content {
|
||||
padding: 4px;
|
||||
}
|
||||
@media (min-width: 767px) {
|
||||
.bootstrap-timepicker-widget.modal {
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.bootstrap-timepicker {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-timepicker .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
10
assets/plugins/bootstrap-timepicker/css/bootstrap-timepicker.min.css
vendored
Normal file
10
assets/plugins/bootstrap-timepicker/css/bootstrap-timepicker.min.css
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/*!
|
||||
* Timepicker Component for Twitter Bootstrap
|
||||
*
|
||||
* Copyright 2013 Joris de Wit
|
||||
*
|
||||
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/.bootstrap-timepicker{position:relative}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu{left:auto;right:0}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before{left:auto;right:12px}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after{left:auto;right:13px}.bootstrap-timepicker .add-on{cursor:pointer}.bootstrap-timepicker .add-on i{display:inline-block;width:16px;height:16px}.bootstrap-timepicker-widget.dropdown-menu{padding:2px 3px 2px 2px}.bootstrap-timepicker-widget.dropdown-menu.open{display:inline-block}.bootstrap-timepicker-widget.dropdown-menu:before{border-bottom:7px solid rgba(0,0,0,0.2);border-left:7px solid transparent;border-right:7px solid transparent;content:"";display:inline-block;left:9px;position:absolute;top:-7px}.bootstrap-timepicker-widget.dropdown-menu:after{border-bottom:6px solid #fff;border-left:6px solid transparent;border-right:6px solid transparent;content:"";display:inline-block;left:10px;position:absolute;top:-6px}.bootstrap-timepicker-widget a.btn,.bootstrap-timepicker-widget input{border-radius:4px}.bootstrap-timepicker-widget table{width:100%;margin:0}.bootstrap-timepicker-widget table td{text-align:center;height:30px;margin:0;padding:2px}.bootstrap-timepicker-widget table td:not(.separator){min-width:30px}.bootstrap-timepicker-widget table td span{width:100%}.bootstrap-timepicker-widget table td a{border:1px transparent solid;width:100%;display:inline-block;margin:0;padding:8px 0;outline:0;color:#333}.bootstrap-timepicker-widget table td a:hover{text-decoration:none;background-color:#eee;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border-color:#ddd}.bootstrap-timepicker-widget table td a i{margin-top:2px}.bootstrap-timepicker-widget table td input{width:25px;margin:0;text-align:center}.bootstrap-timepicker-widget .modal-content{padding:4px}@media(min-width:767px){.bootstrap-timepicker-widget.modal{width:200px;margin-left:-100px}}@media(max-width:767px){.bootstrap-timepicker{width:100%}.bootstrap-timepicker .dropdown-menu{width:100%}}
|
||||
888
assets/plugins/bootstrap-timepicker/js/bootstrap-timepicker.js
vendored
Normal file
888
assets/plugins/bootstrap-timepicker/js/bootstrap-timepicker.js
vendored
Normal file
@@ -0,0 +1,888 @@
|
||||
/*!
|
||||
* Timepicker Component for Twitter Bootstrap
|
||||
*
|
||||
* Copyright 2013 Joris de Wit
|
||||
*
|
||||
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
(function($, window, document, undefined) {
|
||||
'use strict';
|
||||
|
||||
// TIMEPICKER PUBLIC CLASS DEFINITION
|
||||
var Timepicker = function(element, options) {
|
||||
this.widget = '';
|
||||
this.$element = $(element);
|
||||
this.defaultTime = options.defaultTime;
|
||||
this.disableFocus = options.disableFocus;
|
||||
this.isOpen = options.isOpen;
|
||||
this.minuteStep = options.minuteStep;
|
||||
this.modalBackdrop = options.modalBackdrop;
|
||||
this.secondStep = options.secondStep;
|
||||
this.showInputs = options.showInputs;
|
||||
this.showMeridian = options.showMeridian;
|
||||
this.showSeconds = options.showSeconds;
|
||||
this.template = options.template;
|
||||
this.appendWidgetTo = options.appendWidgetTo;
|
||||
|
||||
this._init();
|
||||
};
|
||||
|
||||
Timepicker.prototype = {
|
||||
|
||||
constructor: Timepicker,
|
||||
|
||||
_init: function() {
|
||||
var self = this;
|
||||
|
||||
if (this.$element.parent().hasClass('input-append') || this.$element.parent().hasClass('input-prepend')) {
|
||||
this.$element.parent('.input-append, .input-prepend').find('.add-on').on({
|
||||
'click.timepicker': $.proxy(this.showWidget, this)
|
||||
});
|
||||
this.$element.on({
|
||||
'focus.timepicker': $.proxy(this.highlightUnit, this),
|
||||
'click.timepicker': $.proxy(this.highlightUnit, this),
|
||||
'keydown.timepicker': $.proxy(this.elementKeydown, this),
|
||||
'blur.timepicker': $.proxy(this.blurElement, this)
|
||||
});
|
||||
} else {
|
||||
if (this.template) {
|
||||
this.$element.on({
|
||||
'focus.timepicker': $.proxy(this.showWidget, this),
|
||||
'click.timepicker': $.proxy(this.showWidget, this),
|
||||
'blur.timepicker': $.proxy(this.blurElement, this)
|
||||
});
|
||||
} else {
|
||||
this.$element.on({
|
||||
'focus.timepicker': $.proxy(this.highlightUnit, this),
|
||||
'click.timepicker': $.proxy(this.highlightUnit, this),
|
||||
'keydown.timepicker': $.proxy(this.elementKeydown, this),
|
||||
'blur.timepicker': $.proxy(this.blurElement, this)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.template !== false) {
|
||||
this.$widget = $(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on('click', $.proxy(this.widgetClick, this));
|
||||
} else {
|
||||
this.$widget = false;
|
||||
}
|
||||
|
||||
if (this.showInputs && this.$widget !== false) {
|
||||
this.$widget.find('input').each(function() {
|
||||
$(this).on({
|
||||
'click.timepicker': function() { $(this).select(); },
|
||||
'keydown.timepicker': $.proxy(self.widgetKeydown, self)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.setDefaultTime(this.defaultTime);
|
||||
},
|
||||
|
||||
blurElement: function() {
|
||||
this.highlightedUnit = undefined;
|
||||
this.updateFromElementVal();
|
||||
},
|
||||
|
||||
decrementHour: function() {
|
||||
if (this.showMeridian) {
|
||||
if (this.hour === 1) {
|
||||
this.hour = 12;
|
||||
} else if (this.hour === 12) {
|
||||
this.hour--;
|
||||
|
||||
return this.toggleMeridian();
|
||||
} else if (this.hour === 0) {
|
||||
this.hour = 11;
|
||||
|
||||
return this.toggleMeridian();
|
||||
} else {
|
||||
this.hour--;
|
||||
}
|
||||
} else {
|
||||
if (this.hour === 0) {
|
||||
this.hour = 23;
|
||||
} else {
|
||||
this.hour--;
|
||||
}
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
decrementMinute: function(step) {
|
||||
var newVal;
|
||||
|
||||
if (step) {
|
||||
newVal = this.minute - step;
|
||||
} else {
|
||||
newVal = this.minute - this.minuteStep;
|
||||
}
|
||||
|
||||
if (newVal < 0) {
|
||||
this.decrementHour();
|
||||
this.minute = newVal + 60;
|
||||
} else {
|
||||
this.minute = newVal;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
decrementSecond: function() {
|
||||
var newVal = this.second - this.secondStep;
|
||||
|
||||
if (newVal < 0) {
|
||||
this.decrementMinute(true);
|
||||
this.second = newVal + 60;
|
||||
} else {
|
||||
this.second = newVal;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
elementKeydown: function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 9: //tab
|
||||
this.updateFromElementVal();
|
||||
|
||||
switch (this.highlightedUnit) {
|
||||
case 'hour':
|
||||
e.preventDefault();
|
||||
this.highlightNextUnit();
|
||||
break;
|
||||
case 'minute':
|
||||
if (this.showMeridian || this.showSeconds) {
|
||||
e.preventDefault();
|
||||
this.highlightNextUnit();
|
||||
}
|
||||
break;
|
||||
case 'second':
|
||||
if (this.showMeridian) {
|
||||
e.preventDefault();
|
||||
this.highlightNextUnit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 27: // escape
|
||||
this.updateFromElementVal();
|
||||
break;
|
||||
case 37: // left arrow
|
||||
e.preventDefault();
|
||||
this.highlightPrevUnit();
|
||||
this.updateFromElementVal();
|
||||
break;
|
||||
case 38: // up arrow
|
||||
e.preventDefault();
|
||||
switch (this.highlightedUnit) {
|
||||
case 'hour':
|
||||
this.incrementHour();
|
||||
this.highlightHour();
|
||||
break;
|
||||
case 'minute':
|
||||
this.incrementMinute();
|
||||
this.highlightMinute();
|
||||
break;
|
||||
case 'second':
|
||||
this.incrementSecond();
|
||||
this.highlightSecond();
|
||||
break;
|
||||
case 'meridian':
|
||||
this.toggleMeridian();
|
||||
this.highlightMeridian();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 39: // right arrow
|
||||
e.preventDefault();
|
||||
this.updateFromElementVal();
|
||||
this.highlightNextUnit();
|
||||
break;
|
||||
case 40: // down arrow
|
||||
e.preventDefault();
|
||||
switch (this.highlightedUnit) {
|
||||
case 'hour':
|
||||
this.decrementHour();
|
||||
this.highlightHour();
|
||||
break;
|
||||
case 'minute':
|
||||
this.decrementMinute();
|
||||
this.highlightMinute();
|
||||
break;
|
||||
case 'second':
|
||||
this.decrementSecond();
|
||||
this.highlightSecond();
|
||||
break;
|
||||
case 'meridian':
|
||||
this.toggleMeridian();
|
||||
this.highlightMeridian();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
formatTime: function(hour, minute, second, meridian) {
|
||||
hour = hour < 10 ? '0' + hour : hour;
|
||||
minute = minute < 10 ? '0' + minute : minute;
|
||||
second = second < 10 ? '0' + second : second;
|
||||
|
||||
return hour + ':' + minute + (this.showSeconds ? ':' + second : '') + (this.showMeridian ? ' ' + meridian : '');
|
||||
},
|
||||
|
||||
getCursorPosition: function() {
|
||||
var input = this.$element.get(0);
|
||||
|
||||
if ('selectionStart' in input) {// Standard-compliant browsers
|
||||
|
||||
return input.selectionStart;
|
||||
} else if (document.selection) {// IE fix
|
||||
input.focus();
|
||||
var sel = document.selection.createRange(),
|
||||
selLen = document.selection.createRange().text.length;
|
||||
|
||||
sel.moveStart('character', - input.value.length);
|
||||
|
||||
return sel.text.length - selLen;
|
||||
}
|
||||
},
|
||||
|
||||
getTemplate: function() {
|
||||
var template,
|
||||
hourTemplate,
|
||||
minuteTemplate,
|
||||
secondTemplate,
|
||||
meridianTemplate,
|
||||
templateContent;
|
||||
|
||||
if (this.showInputs) {
|
||||
hourTemplate = '<input type="text" name="hour" class="bootstrap-timepicker-hour" maxlength="2"/>';
|
||||
minuteTemplate = '<input type="text" name="minute" class="bootstrap-timepicker-minute" maxlength="2"/>';
|
||||
secondTemplate = '<input type="text" name="second" class="bootstrap-timepicker-second" maxlength="2"/>';
|
||||
meridianTemplate = '<input type="text" name="meridian" class="bootstrap-timepicker-meridian" maxlength="2"/>';
|
||||
} else {
|
||||
hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
|
||||
minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
|
||||
secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
|
||||
meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
|
||||
}
|
||||
|
||||
templateContent = '<table>'+
|
||||
'<tr>'+
|
||||
'<td><a href="#" data-action="incrementHour"><i class="icon-chevron-up"></i></a></td>'+
|
||||
'<td class="separator"> </td>'+
|
||||
'<td><a href="#" data-action="incrementMinute"><i class="icon-chevron-up"></i></a></td>'+
|
||||
(this.showSeconds ?
|
||||
'<td class="separator"> </td>'+
|
||||
'<td><a href="#" data-action="incrementSecond"><i class="icon-chevron-up"></i></a></td>'
|
||||
: '') +
|
||||
(this.showMeridian ?
|
||||
'<td class="separator"> </td>'+
|
||||
'<td class="meridian-column"><a href="#" data-action="toggleMeridian"><i class="icon-chevron-up"></i></a></td>'
|
||||
: '') +
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td>'+ hourTemplate +'</td> '+
|
||||
'<td class="separator">:</td>'+
|
||||
'<td>'+ minuteTemplate +'</td> '+
|
||||
(this.showSeconds ?
|
||||
'<td class="separator">:</td>'+
|
||||
'<td>'+ secondTemplate +'</td>'
|
||||
: '') +
|
||||
(this.showMeridian ?
|
||||
'<td class="separator"> </td>'+
|
||||
'<td>'+ meridianTemplate +'</td>'
|
||||
: '') +
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td><a href="#" data-action="decrementHour"><i class="icon-chevron-down"></i></a></td>'+
|
||||
'<td class="separator"></td>'+
|
||||
'<td><a href="#" data-action="decrementMinute"><i class="icon-chevron-down"></i></a></td>'+
|
||||
(this.showSeconds ?
|
||||
'<td class="separator"> </td>'+
|
||||
'<td><a href="#" data-action="decrementSecond"><i class="icon-chevron-down"></i></a></td>'
|
||||
: '') +
|
||||
(this.showMeridian ?
|
||||
'<td class="separator"> </td>'+
|
||||
'<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-down"></i></a></td>'
|
||||
: '') +
|
||||
'</tr>'+
|
||||
'</table>';
|
||||
|
||||
switch(this.template) {
|
||||
case 'modal':
|
||||
template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
|
||||
'<div class="modal-header">'+
|
||||
'<a href="#" class="close" data-dismiss="modal">×</a>'+
|
||||
'<h3>Pick a Time</h3>'+
|
||||
'</div>'+
|
||||
'<div class="modal-content">'+
|
||||
templateContent +
|
||||
'</div>'+
|
||||
'<div class="modal-footer">'+
|
||||
'<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
|
||||
'</div>'+
|
||||
'</div>';
|
||||
break;
|
||||
case 'dropdown':
|
||||
template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
|
||||
break;
|
||||
}
|
||||
|
||||
return template;
|
||||
},
|
||||
|
||||
getTime: function() {
|
||||
return this.formatTime(this.hour, this.minute, this.second, this.meridian);
|
||||
},
|
||||
|
||||
hideWidget: function() {
|
||||
if (this.isOpen === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.showInputs) {
|
||||
this.updateFromWidgetInputs();
|
||||
}
|
||||
|
||||
this.$element.trigger({
|
||||
'type': 'hide.timepicker',
|
||||
'time': {
|
||||
'value': this.getTime(),
|
||||
'hours': this.hour,
|
||||
'minutes': this.minute,
|
||||
'seconds': this.second,
|
||||
'meridian': this.meridian
|
||||
}
|
||||
});
|
||||
|
||||
if (this.template === 'modal' && this.$widget.modal) {
|
||||
this.$widget.modal('hide');
|
||||
} else {
|
||||
this.$widget.removeClass('open');
|
||||
}
|
||||
|
||||
$(document).off('mousedown.timepicker');
|
||||
|
||||
this.isOpen = false;
|
||||
},
|
||||
|
||||
highlightUnit: function() {
|
||||
this.position = this.getCursorPosition();
|
||||
if (this.position >= 0 && this.position <= 2) {
|
||||
this.highlightHour();
|
||||
} else if (this.position >= 3 && this.position <= 5) {
|
||||
this.highlightMinute();
|
||||
} else if (this.position >= 6 && this.position <= 8) {
|
||||
if (this.showSeconds) {
|
||||
this.highlightSecond();
|
||||
} else {
|
||||
this.highlightMeridian();
|
||||
}
|
||||
} else if (this.position >= 9 && this.position <= 11) {
|
||||
this.highlightMeridian();
|
||||
}
|
||||
},
|
||||
|
||||
highlightNextUnit: function() {
|
||||
switch (this.highlightedUnit) {
|
||||
case 'hour':
|
||||
this.highlightMinute();
|
||||
break;
|
||||
case 'minute':
|
||||
if (this.showSeconds) {
|
||||
this.highlightSecond();
|
||||
} else if (this.showMeridian){
|
||||
this.highlightMeridian();
|
||||
} else {
|
||||
this.highlightHour();
|
||||
}
|
||||
break;
|
||||
case 'second':
|
||||
if (this.showMeridian) {
|
||||
this.highlightMeridian();
|
||||
} else {
|
||||
this.highlightHour();
|
||||
}
|
||||
break;
|
||||
case 'meridian':
|
||||
this.highlightHour();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
highlightPrevUnit: function() {
|
||||
switch (this.highlightedUnit) {
|
||||
case 'hour':
|
||||
this.highlightMeridian();
|
||||
break;
|
||||
case 'minute':
|
||||
this.highlightHour();
|
||||
break;
|
||||
case 'second':
|
||||
this.highlightMinute();
|
||||
break;
|
||||
case 'meridian':
|
||||
if (this.showSeconds) {
|
||||
this.highlightSecond();
|
||||
} else {
|
||||
this.highlightMinute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
highlightHour: function() {
|
||||
var $element = this.$element.get(0);
|
||||
|
||||
this.highlightedUnit = 'hour';
|
||||
|
||||
if ($element.setSelectionRange) {
|
||||
setTimeout(function() {
|
||||
$element.setSelectionRange(0,2);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
|
||||
highlightMinute: function() {
|
||||
var $element = this.$element.get(0);
|
||||
|
||||
this.highlightedUnit = 'minute';
|
||||
|
||||
if ($element.setSelectionRange) {
|
||||
setTimeout(function() {
|
||||
$element.setSelectionRange(3,5);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
|
||||
highlightSecond: function() {
|
||||
var $element = this.$element.get(0);
|
||||
|
||||
this.highlightedUnit = 'second';
|
||||
|
||||
if ($element.setSelectionRange) {
|
||||
setTimeout(function() {
|
||||
$element.setSelectionRange(6,8);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
|
||||
highlightMeridian: function() {
|
||||
var $element = this.$element.get(0);
|
||||
|
||||
this.highlightedUnit = 'meridian';
|
||||
|
||||
if ($element.setSelectionRange) {
|
||||
if (this.showSeconds) {
|
||||
setTimeout(function() {
|
||||
$element.setSelectionRange(9,11);
|
||||
}, 0);
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
$element.setSelectionRange(6,8);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
incrementHour: function() {
|
||||
if (this.showMeridian) {
|
||||
if (this.hour === 11) {
|
||||
this.hour++;
|
||||
return this.toggleMeridian();
|
||||
} else if (this.hour === 12) {
|
||||
this.hour = 0;
|
||||
}
|
||||
}
|
||||
if (this.hour === 23) {
|
||||
this.hour = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
this.hour++;
|
||||
this.update();
|
||||
},
|
||||
|
||||
incrementMinute: function(step) {
|
||||
var newVal;
|
||||
|
||||
if (step) {
|
||||
newVal = this.minute + step;
|
||||
} else {
|
||||
newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
|
||||
}
|
||||
|
||||
if (newVal > 59) {
|
||||
this.incrementHour();
|
||||
this.minute = newVal - 60;
|
||||
} else {
|
||||
this.minute = newVal;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
incrementSecond: function() {
|
||||
var newVal = this.second + this.secondStep - (this.second % this.secondStep);
|
||||
|
||||
if (newVal > 59) {
|
||||
this.incrementMinute(true);
|
||||
this.second = newVal - 60;
|
||||
} else {
|
||||
this.second = newVal;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
$('document').off('.timepicker');
|
||||
if (this.$widget) {
|
||||
this.$widget.remove();
|
||||
}
|
||||
delete this.$element.data().timepicker;
|
||||
},
|
||||
|
||||
setDefaultTime: function(defaultTime){
|
||||
if (!this.$element.val()) {
|
||||
if (defaultTime === 'current') {
|
||||
var dTime = new Date(),
|
||||
hours = dTime.getHours(),
|
||||
minutes = Math.floor(dTime.getMinutes() / this.minuteStep) * this.minuteStep,
|
||||
seconds = Math.floor(dTime.getSeconds() / this.secondStep) * this.secondStep,
|
||||
meridian = 'AM';
|
||||
|
||||
if (this.showMeridian) {
|
||||
if (hours === 0) {
|
||||
hours = 12;
|
||||
} else if (hours >= 12) {
|
||||
if (hours > 12) {
|
||||
hours = hours - 12;
|
||||
}
|
||||
meridian = 'PM';
|
||||
} else {
|
||||
meridian = 'AM';
|
||||
}
|
||||
}
|
||||
|
||||
this.hour = hours;
|
||||
this.minute = minutes;
|
||||
this.second = seconds;
|
||||
this.meridian = meridian;
|
||||
|
||||
this.update();
|
||||
|
||||
} else if (defaultTime === false) {
|
||||
this.hour = 0;
|
||||
this.minute = 0;
|
||||
this.second = 0;
|
||||
this.meridian = 'AM';
|
||||
} else {
|
||||
this.setTime(defaultTime);
|
||||
}
|
||||
} else {
|
||||
this.updateFromElementVal();
|
||||
}
|
||||
},
|
||||
|
||||
setTime: function(time) {
|
||||
var arr,
|
||||
timeArray;
|
||||
|
||||
if (this.showMeridian) {
|
||||
arr = time.split(' ');
|
||||
timeArray = arr[0].split(':');
|
||||
this.meridian = arr[1];
|
||||
} else {
|
||||
timeArray = time.split(':');
|
||||
}
|
||||
|
||||
this.hour = parseInt(timeArray[0], 10);
|
||||
this.minute = parseInt(timeArray[1], 10);
|
||||
this.second = parseInt(timeArray[2], 10);
|
||||
|
||||
if (isNaN(this.hour)) {
|
||||
this.hour = 0;
|
||||
}
|
||||
if (isNaN(this.minute)) {
|
||||
this.minute = 0;
|
||||
}
|
||||
|
||||
if (this.showMeridian) {
|
||||
if (this.hour > 12) {
|
||||
this.hour = 12;
|
||||
} else if (this.hour < 1) {
|
||||
this.hour = 12;
|
||||
}
|
||||
|
||||
if (this.meridian === 'am' || this.meridian === 'a') {
|
||||
this.meridian = 'AM';
|
||||
} else if (this.meridian === 'pm' || this.meridian === 'p') {
|
||||
this.meridian = 'PM';
|
||||
}
|
||||
|
||||
if (this.meridian !== 'AM' && this.meridian !== 'PM') {
|
||||
this.meridian = 'AM';
|
||||
}
|
||||
} else {
|
||||
if (this.hour >= 24) {
|
||||
this.hour = 23;
|
||||
} else if (this.hour < 0) {
|
||||
this.hour = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.minute < 0) {
|
||||
this.minute = 0;
|
||||
} else if (this.minute >= 60) {
|
||||
this.minute = 59;
|
||||
}
|
||||
|
||||
if (this.showSeconds) {
|
||||
if (isNaN(this.second)) {
|
||||
this.second = 0;
|
||||
} else if (this.second < 0) {
|
||||
this.second = 0;
|
||||
} else if (this.second >= 60) {
|
||||
this.second = 59;
|
||||
}
|
||||
}
|
||||
|
||||
this.update();
|
||||
},
|
||||
|
||||
showWidget: function() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.$element.is(':disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
$(document).on('mousedown.timepicker', function (e) {
|
||||
// Clicked outside the timepicker, hide it
|
||||
if ($(e.target).closest('.bootstrap-timepicker-widget').length === 0) {
|
||||
self.hideWidget();
|
||||
}
|
||||
});
|
||||
|
||||
this.$element.trigger({
|
||||
'type': 'show.timepicker',
|
||||
'time': {
|
||||
'value': this.getTime(),
|
||||
'hours': this.hour,
|
||||
'minutes': this.minute,
|
||||
'seconds': this.second,
|
||||
'meridian': this.meridian
|
||||
}
|
||||
});
|
||||
|
||||
if (this.disableFocus) {
|
||||
this.$element.blur();
|
||||
}
|
||||
|
||||
this.updateFromElementVal();
|
||||
|
||||
if (this.template === 'modal' && this.$widget.modal) {
|
||||
this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
|
||||
} else {
|
||||
if (this.isOpen === false) {
|
||||
this.$widget.addClass('open');
|
||||
}
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
},
|
||||
|
||||
toggleMeridian: function() {
|
||||
this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
|
||||
this.update();
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.$element.trigger({
|
||||
'type': 'changeTime.timepicker',
|
||||
'time': {
|
||||
'value': this.getTime(),
|
||||
'hours': this.hour,
|
||||
'minutes': this.minute,
|
||||
'seconds': this.second,
|
||||
'meridian': this.meridian
|
||||
}
|
||||
});
|
||||
|
||||
this.updateElement();
|
||||
this.updateWidget();
|
||||
},
|
||||
|
||||
updateElement: function() {
|
||||
this.$element.val(this.getTime()).change();
|
||||
},
|
||||
|
||||
updateFromElementVal: function() {
|
||||
var val = this.$element.val();
|
||||
|
||||
if (val) {
|
||||
this.setTime(val);
|
||||
}
|
||||
},
|
||||
|
||||
updateWidget: function() {
|
||||
if (this.$widget === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hour = this.hour < 10 ? '0' + this.hour : this.hour,
|
||||
minute = this.minute < 10 ? '0' + this.minute : this.minute,
|
||||
second = this.second < 10 ? '0' + this.second : this.second;
|
||||
|
||||
if (this.showInputs) {
|
||||
this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
|
||||
this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
|
||||
|
||||
if (this.showSeconds) {
|
||||
this.$widget.find('input.bootstrap-timepicker-second').val(second);
|
||||
}
|
||||
if (this.showMeridian) {
|
||||
this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
|
||||
}
|
||||
} else {
|
||||
this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
|
||||
this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
|
||||
|
||||
if (this.showSeconds) {
|
||||
this.$widget.find('span.bootstrap-timepicker-second').text(second);
|
||||
}
|
||||
if (this.showMeridian) {
|
||||
this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateFromWidgetInputs: function() {
|
||||
if (this.$widget === false) {
|
||||
return;
|
||||
}
|
||||
var time = $('input.bootstrap-timepicker-hour', this.$widget).val() + ':' +
|
||||
$('input.bootstrap-timepicker-minute', this.$widget).val() +
|
||||
(this.showSeconds ? ':' + $('input.bootstrap-timepicker-second', this.$widget).val() : '') +
|
||||
(this.showMeridian ? ' ' + $('input.bootstrap-timepicker-meridian', this.$widget).val() : '');
|
||||
|
||||
this.setTime(time);
|
||||
},
|
||||
|
||||
widgetClick: function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
var action = $(e.target).closest('a').data('action');
|
||||
if (action) {
|
||||
this[action]();
|
||||
}
|
||||
},
|
||||
|
||||
widgetKeydown: function(e) {
|
||||
var $input = $(e.target).closest('input'),
|
||||
name = $input.attr('name');
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 9: //tab
|
||||
if (this.showMeridian) {
|
||||
if (name === 'meridian') {
|
||||
return this.hideWidget();
|
||||
}
|
||||
} else {
|
||||
if (this.showSeconds) {
|
||||
if (name === 'second') {
|
||||
return this.hideWidget();
|
||||
}
|
||||
} else {
|
||||
if (name === 'minute') {
|
||||
return this.hideWidget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateFromWidgetInputs();
|
||||
break;
|
||||
case 27: // escape
|
||||
this.hideWidget();
|
||||
break;
|
||||
case 38: // up arrow
|
||||
e.preventDefault();
|
||||
switch (name) {
|
||||
case 'hour':
|
||||
this.incrementHour();
|
||||
break;
|
||||
case 'minute':
|
||||
this.incrementMinute();
|
||||
break;
|
||||
case 'second':
|
||||
this.incrementSecond();
|
||||
break;
|
||||
case 'meridian':
|
||||
this.toggleMeridian();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 40: // down arrow
|
||||
e.preventDefault();
|
||||
switch (name) {
|
||||
case 'hour':
|
||||
this.decrementHour();
|
||||
break;
|
||||
case 'minute':
|
||||
this.decrementMinute();
|
||||
break;
|
||||
case 'second':
|
||||
this.decrementSecond();
|
||||
break;
|
||||
case 'meridian':
|
||||
this.toggleMeridian();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//TIMEPICKER PLUGIN DEFINITION
|
||||
$.fn.timepicker = function(option) {
|
||||
var args = Array.apply(null, arguments);
|
||||
args.shift();
|
||||
return this.each(function() {
|
||||
var $this = $(this),
|
||||
data = $this.data('timepicker'),
|
||||
options = typeof option === 'object' && option;
|
||||
|
||||
if (!data) {
|
||||
$this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
|
||||
}
|
||||
|
||||
if (typeof option === 'string') {
|
||||
data[option].apply(data, args);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.timepicker.defaults = {
|
||||
defaultTime: 'current',
|
||||
disableFocus: false,
|
||||
isOpen: false,
|
||||
minuteStep: 15,
|
||||
modalBackdrop: false,
|
||||
secondStep: 15,
|
||||
showSeconds: false,
|
||||
showInputs: true,
|
||||
showMeridian: true,
|
||||
template: 'dropdown',
|
||||
appendWidgetTo: '.bootstrap-timepicker'
|
||||
};
|
||||
|
||||
$.fn.timepicker.Constructor = Timepicker;
|
||||
|
||||
})(jQuery, window, document);
|
||||
5
assets/plugins/bootstrap-timepicker/js/bootstrap-timepicker.min.js
vendored
Normal file
5
assets/plugins/bootstrap-timepicker/js/bootstrap-timepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
145
assets/plugins/bootstrap-timepicker/less/timepicker.less
Normal file
145
assets/plugins/bootstrap-timepicker/less/timepicker.less
Normal file
@@ -0,0 +1,145 @@
|
||||
/*!
|
||||
* Timepicker Component for Twitter Bootstrap
|
||||
*
|
||||
* Copyright 2013 Joris de Wit
|
||||
*
|
||||
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
.bootstrap-timepicker {
|
||||
position: relative;
|
||||
|
||||
&.pull-right {
|
||||
.bootstrap-timepicker-widget {
|
||||
&.dropdown-menu {
|
||||
left: auto;
|
||||
right: 0;
|
||||
|
||||
&:before {
|
||||
left: auto;
|
||||
right: 12px;
|
||||
}
|
||||
&:after {
|
||||
left: auto;
|
||||
right: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-on {
|
||||
cursor: pointer;
|
||||
i {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bootstrap-timepicker-widget {
|
||||
&.dropdown-menu {
|
||||
padding: 2px 3px 2px 2px;
|
||||
&.open {
|
||||
display: inline-block;
|
||||
}
|
||||
&:before {
|
||||
border-bottom: 7px solid rgba(0, 0, 0, 0.2);
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
left: 9px;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
}
|
||||
&:after {
|
||||
border-bottom: 6px solid #FFFFFF;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
a.btn, input {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
|
||||
&:not(.separator) {
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 100%;
|
||||
}
|
||||
a {
|
||||
border: 1px transparent solid;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 8px 0;
|
||||
outline: 0;
|
||||
color: #333;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: #eee;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
input {
|
||||
width: 25px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bootstrap-timepicker-widget .modal-content {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 767px) {
|
||||
.bootstrap-timepicker-widget.modal {
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.bootstrap-timepicker {
|
||||
width: 100%;
|
||||
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
31
assets/plugins/bootstrap-timepicker/package.json
Normal file
31
assets/plugins/bootstrap-timepicker/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "bootstrap-timepicker",
|
||||
"description": "Timepicker widget for Twitter Bootstrap 2.*",
|
||||
"version": "0.2.3",
|
||||
"homepage": "http://jdewit.github.com/bootstrap-timepicker",
|
||||
"author": {
|
||||
"name": "Joris de Wit",
|
||||
"email": "joris.w.dewit@gmail.com",
|
||||
"url": "http://jorisdewit.ca"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jdewit/bootstrap-timepicker"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "bower install; grunt test;"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/jdewit/bootstrap-timepicker/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt-cli": "~0.1",
|
||||
"grunt": "~0.4.1",
|
||||
"bower": "latest",
|
||||
"grunt-contrib-less": "~0.5.0",
|
||||
"grunt-contrib-jshint": "~0.4.3",
|
||||
"grunt-contrib-uglify": "~0.2.0",
|
||||
"grunt-contrib-jasmine": "~0.4.2",
|
||||
"grunt-contrib-watch": "~0.4.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
describe('Keyboard events feature', function() {
|
||||
'use strict';
|
||||
|
||||
var $input1,
|
||||
$input2,
|
||||
$input3,
|
||||
$timepicker1,
|
||||
$timepicker2,
|
||||
$timepicker3,
|
||||
tp1,
|
||||
tp2,
|
||||
tp3;
|
||||
|
||||
beforeEach(function () {
|
||||
loadFixtures('timepicker.html');
|
||||
|
||||
$input1 = $('#timepicker1');
|
||||
$timepicker1 = $input1.timepicker();
|
||||
tp1 = $timepicker1.data('timepicker');
|
||||
|
||||
$input2 = $('#timepicker2');
|
||||
$timepicker2 = $input2.timepicker({
|
||||
template: 'modal',
|
||||
showSeconds: true,
|
||||
minuteStep: 30,
|
||||
secondStep: 30,
|
||||
defaultTime: false
|
||||
});
|
||||
tp2 = $timepicker2.data('timepicker');
|
||||
|
||||
$input3 = $('#timepicker3');
|
||||
$timepicker3 = $input3.timepicker({
|
||||
defaultTime: '23:15:20',
|
||||
showMeridian: false,
|
||||
showSeconds: true,
|
||||
template: false
|
||||
});
|
||||
tp3 = $timepicker3.data('timepicker');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$input1.data('timepicker').remove();
|
||||
$input2.data('timepicker').remove();
|
||||
$input3.data('timepicker').remove();
|
||||
$input1.remove();
|
||||
$input2.remove();
|
||||
$input3.remove();
|
||||
});
|
||||
|
||||
it('should be able to control element by the arrow keys', function() {
|
||||
tp1.setTime('11:30 AM');
|
||||
tp1.update();
|
||||
|
||||
$input1.trigger('focus');
|
||||
|
||||
if (tp1.highlightedUnit !== 'hour') {
|
||||
tp1.highlightHour();
|
||||
}
|
||||
|
||||
expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted by default');
|
||||
// hours
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 38 //up
|
||||
});
|
||||
expect(tp1.getTime()).toBe('12:30 PM', '1');
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:30 AM', '2');
|
||||
expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 39 //right
|
||||
});
|
||||
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted');
|
||||
|
||||
//minutes
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 38 //up
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:45 AM', '3');
|
||||
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 1');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:30 AM', '4');
|
||||
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 2');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 39 //right
|
||||
});
|
||||
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
|
||||
|
||||
//meridian
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 38 //up
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:30 PM', '5');
|
||||
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:30 AM', '6');
|
||||
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 37 //left
|
||||
});
|
||||
expect(tp1.highlightedUnit).toBe('minute', 'minutes should be highlighted');
|
||||
|
||||
// minutes
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('11:15 AM', '7');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 37 //left
|
||||
});
|
||||
expect(tp1.highlightedUnit).toBe('hour', 'hours should be highlighted');
|
||||
|
||||
// hours
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('10:15 AM', '8');
|
||||
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 37 //left
|
||||
});
|
||||
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
|
||||
|
||||
// meridian
|
||||
$input1.trigger({
|
||||
'type': 'keydown',
|
||||
'keyCode': 40 //down
|
||||
});
|
||||
expect(tp1.getTime()).toBe('10:15 PM', '9');
|
||||
});
|
||||
|
||||
it('should be able to change time via widget inputs in a dropdown', function() {
|
||||
var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
|
||||
$minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
|
||||
$meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
|
||||
eventCount = 0,
|
||||
time;
|
||||
|
||||
|
||||
tp1.setTime('9:30 AM');
|
||||
tp1.update();
|
||||
$input1.parents('div').find('.add-on').click();
|
||||
|
||||
$input1.timepicker().on('changeTime.timepicker', function(e) {
|
||||
eventCount++;
|
||||
time = e.time.value;
|
||||
});
|
||||
|
||||
expect(tp1.isOpen).toBe(true);
|
||||
|
||||
$hourInput.trigger('focus');
|
||||
$hourInput.autotype('{{back}}{{back}}11{{tab}}');
|
||||
|
||||
expect(tp1.hour).toBe(11);
|
||||
expect(eventCount).toBe(1, 'incorrect update events thrown');
|
||||
expect(time).toBe('11:30 AM');
|
||||
|
||||
$minuteInput.autotype('{{back}}{{back}}45{{tab}}');
|
||||
|
||||
expect(tp1.minute).toBe(45);
|
||||
expect(eventCount).toBe(2, 'incorrect update events thrown');
|
||||
expect(time).toBe('11:45 AM');
|
||||
|
||||
$meridianInput.autotype('{{back}}{{back}}pm{{tab}}');
|
||||
|
||||
expect(tp1.meridian).toBe('PM');
|
||||
expect(eventCount).toBe(3, 'incorrect update events thrown');
|
||||
expect(time).toBe('11:45 PM');
|
||||
});
|
||||
|
||||
it('should still be empty if input is empty', function() {
|
||||
$input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{tab}}');
|
||||
|
||||
expect($input1.val()).toBe('');
|
||||
});
|
||||
it('should allow time to be changed via widget inputs in a modal', function() {
|
||||
//tp2.setTime('9:30 AM');
|
||||
//tp2.update();
|
||||
//$input2.parents('div').find('.add-on').click();
|
||||
|
||||
//var $hourInput = $('body').find('input.bootstrap-timepicker-hour'),
|
||||
//$minuteInput = $('body').find('input.bootstrap-timepicker-minute'),
|
||||
//$secondInput = $('body').find('input.bootstrap-timepicker-second'),
|
||||
//$meridianInput = $('body').find('input.bootstrap-timepicker-meridian');
|
||||
|
||||
//$hourInput.autotype('{{back}}{{back}}2');
|
||||
//$hourInput.trigger({
|
||||
//'type': 'keydown',
|
||||
//'keyCode': 9 //tab
|
||||
//});
|
||||
|
||||
//expect(tp2.getTime()).toBe('02:30:00 AM');
|
||||
|
||||
|
||||
//$minuteInput.autotype('{{back}}{{back}}0');
|
||||
//$minuteInput.trigger({
|
||||
//'type': 'keydown',
|
||||
//'keyCode': 9 //tab
|
||||
//});
|
||||
|
||||
//expect(tp2.getTime()).toBe('02:00:00 AM');
|
||||
|
||||
//$secondInput.autotype('{{back}}{{back}}30');
|
||||
//$secondInput.trigger({
|
||||
//'type': 'keydown',
|
||||
//'keyCode': 9 //tab
|
||||
//});
|
||||
|
||||
//expect(tp2.getTime()).toBe('02:00:30 AM');
|
||||
|
||||
//$meridianInput.autotype('{{back}}{{back}}p');
|
||||
//$meridianInput.trigger({
|
||||
//'type': 'keydown',
|
||||
//'keyCode': 9 //tab
|
||||
//});
|
||||
|
||||
//expect(tp2.getTime()).toBe('02:00:30 PM');
|
||||
});
|
||||
|
||||
it('should be 12:00 AM if 00:00 AM is entered', function() {
|
||||
//$input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}0:0 AM');
|
||||
//$input1.trigger({
|
||||
//'type': 'keydown',
|
||||
//'keyCode': 9 //tab
|
||||
//});
|
||||
|
||||
//expect(tp1.getTime()).toBe('12:00 AM');
|
||||
});
|
||||
|
||||
it('should validate input', function() {
|
||||
//var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
|
||||
//$minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
|
||||
//$meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
|
||||
//$input3 = tp3.$element;
|
||||
|
||||
//tp1.setTime('11:30 AM');
|
||||
//tp1.update();
|
||||
|
||||
//$hourInput.autotype('{{back}}{{back}}13');
|
||||
//tp1.updateFromWidgetInputs();
|
||||
//expect(tp1.getTime()).toBe('12:30 AM');
|
||||
|
||||
//$minuteInput.autotype('{{back}}{{back}}60');
|
||||
//tp1.updateFromWidgetInputs();
|
||||
//expect(tp1.getTime()).toBe('12:59 AM');
|
||||
|
||||
//$meridianInput.autotype('{{back}}{{back}}dk');
|
||||
//tp1.updateFromWidgetInputs();
|
||||
//expect(tp1.getTime()).toBe('12:59 AM');
|
||||
|
||||
//$meridianInput.autotype('{{back}}{{back}}p');
|
||||
//tp1.updateFromWidgetInputs();
|
||||
//expect(tp1.getTime()).toBe('12:59 PM');
|
||||
|
||||
//$input3.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}25:60:60');
|
||||
//tp3.updateFromElementVal();
|
||||
//expect(tp3.getTime()).toBe('23:59:59');
|
||||
});
|
||||
});
|
||||
262
assets/plugins/bootstrap-timepicker/spec/js/MouseEventsSpec.js
Normal file
262
assets/plugins/bootstrap-timepicker/spec/js/MouseEventsSpec.js
Normal file
@@ -0,0 +1,262 @@
|
||||
describe('Mouse events feature', function() {
|
||||
'use strict';
|
||||
|
||||
var $input1,
|
||||
$input2,
|
||||
$input3,
|
||||
$input4,
|
||||
$timepicker1,
|
||||
$timepicker2,
|
||||
$timepicker3,
|
||||
$timepicker4,
|
||||
tp1,
|
||||
tp2,
|
||||
tp3,
|
||||
tp4;
|
||||
|
||||
beforeEach(function () {
|
||||
loadFixtures('timepicker.html');
|
||||
|
||||
$input1 = $('#timepicker1');
|
||||
$timepicker1 = $input1.timepicker();
|
||||
tp1 = $timepicker1.data('timepicker');
|
||||
|
||||
$input2 = $('#timepicker2');
|
||||
$timepicker2 = $input2.timepicker({
|
||||
template: 'modal',
|
||||
showSeconds: true,
|
||||
minuteStep: 30,
|
||||
secondStep: 30,
|
||||
defaultTime: false
|
||||
});
|
||||
tp2 = $timepicker2.data('timepicker');
|
||||
|
||||
$input3 = $('#timepicker3');
|
||||
$timepicker3 = $input3.timepicker({
|
||||
defaultTime: '23:15:20',
|
||||
showMeridian: false,
|
||||
showSeconds: true
|
||||
});
|
||||
tp3 = $timepicker3.data('timepicker');
|
||||
|
||||
$input4 = $('#timepicker4');
|
||||
$timepicker4 = $input4.timepicker({
|
||||
minuteStep: 5,
|
||||
showInputs: false,
|
||||
showMeridian: true,
|
||||
template: 'modal',
|
||||
disableFocus: true
|
||||
});
|
||||
tp4 = $timepicker4.data('timepicker');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$input1.data('timepicker').remove();
|
||||
$input2.data('timepicker').remove();
|
||||
$input3.data('timepicker').remove();
|
||||
$input4.data('timepicker').remove();
|
||||
$input1.remove();
|
||||
$input2.remove();
|
||||
$input3.remove();
|
||||
$input4.remove();
|
||||
});
|
||||
|
||||
it('should be shown and trigger show events on input click', function() {
|
||||
var showEvents = 0;
|
||||
|
||||
$input1.on('show.timepicker', function() {
|
||||
showEvents++;
|
||||
});
|
||||
|
||||
$input1.parents('div').find('.add-on').trigger('click');
|
||||
|
||||
expect(tp1.isOpen).toBe(true);
|
||||
expect(showEvents).toBe(1);
|
||||
});
|
||||
|
||||
it('should be hidden and trigger hide events on click outside of widget', function() {
|
||||
var hideEvents = 0,
|
||||
time;
|
||||
$input1.val('11:30 AM');
|
||||
|
||||
$input1.on('hide.timepicker', function(e) {
|
||||
hideEvents++;
|
||||
|
||||
time = e.time.value;
|
||||
});
|
||||
|
||||
$input1.parents('div').find('.add-on').trigger('click');
|
||||
expect(tp1.isOpen).toBe(true);
|
||||
|
||||
tp1.$widget.find('.bootstrap-timepicker-hour').trigger('mousedown');
|
||||
$('body').trigger('mousedown');
|
||||
|
||||
expect(tp1.isOpen).toBe(false, 'widget is still open');
|
||||
expect(hideEvents).toBe(1, 'hide event was not thrown once');
|
||||
expect(time).toBe('11:30 AM');
|
||||
|
||||
});
|
||||
|
||||
it('should increment hour on button click', function() {
|
||||
tp1.setTime('11:30 AM');
|
||||
tp1.update();
|
||||
|
||||
tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
|
||||
|
||||
expect(tp1.getTime()).toBe('12:30 PM');
|
||||
|
||||
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
|
||||
expect(tp2.getTime()).toBe('01:00:00 AM');
|
||||
});
|
||||
|
||||
it('should decrement hour on button click', function() {
|
||||
tp1.setTime('12:30 PM');
|
||||
tp1.update();
|
||||
|
||||
tp1.$widget.find('a[data-action="decrementHour"]').trigger('click');
|
||||
|
||||
expect(tp1.getTime()).toBe('11:30 AM', 'meridian isnt toggling');
|
||||
|
||||
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
|
||||
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
|
||||
tp2.$widget.find('a[data-action="decrementHour"]').trigger('click');
|
||||
expect(tp2.getTime()).toBe('01:00:00 AM');
|
||||
});
|
||||
|
||||
it('should increment minute on button click', function() {
|
||||
tp1.setTime('11:30 AM');
|
||||
tp1.update();
|
||||
tp4.setTime('11:30 AM');
|
||||
tp4.update();
|
||||
|
||||
tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
|
||||
expect(tp1.getTime()).toBe('11:45 AM');
|
||||
|
||||
tp2.$widget.find('a[data-action="incrementMinute"]').trigger('click');
|
||||
expect(tp2.getTime()).toBe('00:30:00 AM');
|
||||
|
||||
$input4.trigger('click');
|
||||
tp4.$widget.find('a[data-action="incrementMinute"]').trigger('click');
|
||||
tp4.$widget.find('a[data-action="decrementHour"]').trigger('click');
|
||||
$input4.closest('modal').find('.btn-primary').trigger('click');
|
||||
expect(tp4.getTime()).toBe('10:35 AM');
|
||||
expect($input4.val()).toBe('10:35 AM');
|
||||
});
|
||||
|
||||
it('should decrement minute on button click', function() {
|
||||
tp1.setTime('12:30 PM');
|
||||
tp1.update();
|
||||
tp4.setTime('11:30 AM');
|
||||
tp4.update();
|
||||
|
||||
tp1.$widget.find('a[data-action="decrementMinute"]').trigger('click');
|
||||
expect(tp1.getTime()).toBe('12:15 PM');
|
||||
|
||||
tp4.$widget.find('a[data-action="decrementMinute"]').trigger('click');
|
||||
expect(tp4.getTime()).toBe('11:25 AM');
|
||||
});
|
||||
|
||||
it('should be 11:30:00 PM if minute is decremented on empty input', function() {
|
||||
tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
|
||||
expect(tp2.getTime()).toBe('11:30:00 PM');
|
||||
});
|
||||
|
||||
it('should increment second on button click', function() {
|
||||
tp2.setTime('11:30:15 AM');
|
||||
tp2.update();
|
||||
|
||||
tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
|
||||
|
||||
expect(tp2.getTime()).toBe('11:30:30 AM');
|
||||
});
|
||||
|
||||
it('should decrement second on button click', function() {
|
||||
tp2.setTime('12:30:15 PM');
|
||||
tp2.update();
|
||||
|
||||
});
|
||||
|
||||
it('should be 11:30:00 PM if minute is decremented on empty input', function() {
|
||||
tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
|
||||
expect(tp2.getTime()).toBe('11:30:00 PM');
|
||||
});
|
||||
|
||||
it('should increment second on button click', function() {
|
||||
tp2.setTime('11:30:15 AM');
|
||||
tp2.update();
|
||||
|
||||
tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
|
||||
|
||||
expect(tp2.getTime()).toBe('11:30:30 AM');
|
||||
});
|
||||
|
||||
it('should decrement second on button click', function() {
|
||||
tp2.setTime('12:30:15 PM');
|
||||
tp2.update();
|
||||
|
||||
tp2.$widget.find('a[data-action="decrementSecond"]').trigger('click');
|
||||
|
||||
expect(tp2.getTime()).toBe('12:29:45 PM');
|
||||
});
|
||||
|
||||
it('should toggle meridian on button click', function() {
|
||||
tp1.setTime('12:30 PM');
|
||||
tp1.update();
|
||||
|
||||
tp1.$widget.find('a[data-action="toggleMeridian"]').first().trigger('click');
|
||||
expect(tp1.getTime()).toBe('12:30 AM');
|
||||
tp1.$widget.find('a[data-action="toggleMeridian"]').last().trigger('click');
|
||||
expect(tp1.getTime()).toBe('12:30 PM');
|
||||
});
|
||||
|
||||
|
||||
it('should trigger changeTime event if time is changed', function() {
|
||||
var eventCount = 0,
|
||||
time;
|
||||
|
||||
$input1.timepicker().on('changeTime.timepicker', function(e) {
|
||||
eventCount++;
|
||||
time = e.time.value;
|
||||
});
|
||||
|
||||
tp1.setTime('11:30 AM');
|
||||
|
||||
expect(eventCount).toBe(1);
|
||||
expect(time).toBe('11:30 AM');
|
||||
|
||||
tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
|
||||
|
||||
expect(eventCount).toBe(2);
|
||||
expect(tp1.getTime()).toBe('12:30 PM');
|
||||
expect(time).toBe('12:30 PM');
|
||||
|
||||
tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
|
||||
|
||||
expect(eventCount).toBe(3);
|
||||
expect(tp1.getTime()).toBe('12:45 PM');
|
||||
});
|
||||
|
||||
it('should highlight widget inputs on click', function() {
|
||||
//TODO;
|
||||
//tp1.setTime('11:55 AM');
|
||||
//tp1.update();
|
||||
|
||||
//$input1.parents('.bootstrap-timepicker').find('.add-on').trigger('click');
|
||||
//expect(tp1.isOpen).toBe(true);
|
||||
//expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('11');
|
||||
//tp1.$widget.find('.bootstrap-timepicker-hour').trigger('click');
|
||||
//var hour1 = window.getSelection().toString();
|
||||
////var range = window.getSelection().getRangeAt(0);
|
||||
////var hour1 = range.extractContents();
|
||||
|
||||
//expect(hour1).toBe('11', 'hour input not being highlighted');
|
||||
|
||||
//tp1.$widget.find('.bootstrap-timepicker-minute').trigger('click');
|
||||
//var minute1 = window.getSelection().toString();
|
||||
//expect(minute1).toBe('55', 'minute input not being highlighted');
|
||||
|
||||
//tp1.$widget.find('.bootstrap-timepicker-meridian').trigger('click');
|
||||
//var meridian1 = window.getSelection().toString();
|
||||
//expect(meridian1).toBe('AM', 'meridian input not being highlighted');
|
||||
});
|
||||
});
|
||||
400
assets/plugins/bootstrap-timepicker/spec/js/TimepickerSpec.js
Normal file
400
assets/plugins/bootstrap-timepicker/spec/js/TimepickerSpec.js
Normal file
@@ -0,0 +1,400 @@
|
||||
describe('Timepicker feature', function() {
|
||||
'use strict';
|
||||
|
||||
var $input1,
|
||||
$input2,
|
||||
$input3,
|
||||
$timepicker1,
|
||||
$timepicker2,
|
||||
$timepicker3,
|
||||
tp1,
|
||||
tp2,
|
||||
tp3;
|
||||
|
||||
beforeEach(function () {
|
||||
loadFixtures('timepicker.html');
|
||||
|
||||
$input1 = $('#timepicker1');
|
||||
$timepicker1 = $input1.timepicker();
|
||||
tp1 = $timepicker1.data('timepicker');
|
||||
|
||||
$input2 = $('#timepicker2');
|
||||
$timepicker2 = $input2.timepicker({
|
||||
template: 'modal',
|
||||
showSeconds: true,
|
||||
minuteStep: 30,
|
||||
secondStep: 30,
|
||||
defaultTime: false
|
||||
});
|
||||
tp2 = $timepicker2.data('timepicker');
|
||||
|
||||
$input3 = $('#timepicker3');
|
||||
$timepicker3 = $input3.timepicker({
|
||||
showMeridian: false,
|
||||
showSeconds: true,
|
||||
defaultTime: '13:25:15'
|
||||
});
|
||||
tp3 = $timepicker3.data('timepicker');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if ($input1.data('timepicker') !== undefined) {
|
||||
$input1.data('timepicker').remove();
|
||||
}
|
||||
if ($input2.data('timepicker') !== undefined) {
|
||||
$input2.data('timepicker').remove();
|
||||
}
|
||||
if ($input3.data('timepicker') !== undefined) {
|
||||
$input3.data('timepicker').remove();
|
||||
}
|
||||
$input1.remove();
|
||||
$input2.remove();
|
||||
$input3.remove();
|
||||
});
|
||||
|
||||
it('should be available on the jquery object', function() {
|
||||
expect($.fn.timepicker).toBeDefined();
|
||||
});
|
||||
|
||||
it('should be chainable', function() {
|
||||
expect($timepicker1).toBe($input1);
|
||||
});
|
||||
|
||||
it('should have sensible defaults', function() {
|
||||
expect(tp1.defaultTime).toBeTruthy();
|
||||
expect(tp1.minuteStep).toBe(15);
|
||||
expect(tp1.secondStep).toBe(15);
|
||||
expect(tp1.disableFocus).toBe(false);
|
||||
expect(tp1.showSeconds).toBe(false);
|
||||
expect(tp1.showInputs).toBe(true);
|
||||
expect(tp1.showMeridian).toBe(true);
|
||||
expect(tp1.template).toBe('dropdown');
|
||||
expect(tp1.modalBackdrop).toBe(false);
|
||||
expect(tp1.modalBackdrop).toBe(false);
|
||||
expect(tp1.isOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow user to configure defaults', function() {
|
||||
expect(tp2.template).toBe('modal');
|
||||
expect(tp2.minuteStep).toBe(30);
|
||||
});
|
||||
|
||||
it('should be configurable with data attributes', function() {
|
||||
$('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');
|
||||
|
||||
var $customInput = $('body').find('#customTimepicker'),
|
||||
tpCustom = $customInput.timepicker().data('timepicker');
|
||||
|
||||
expect($('body').find('#customTimepicker').length).toBe(1);
|
||||
expect(tpCustom.template).toBe('modal');
|
||||
expect(tpCustom.minuteStep).toBe(30, 'data-minute-step not working');
|
||||
expect(tpCustom.modalBackdrop).toBe(true, 'data-modal-backdrop not working');
|
||||
expect(tpCustom.showMeridian).toBe(true, 'data-show-meridian not working');
|
||||
|
||||
tpCustom.remove();
|
||||
});
|
||||
|
||||
it('should have current time by default', function() {
|
||||
var dTime = new Date(),
|
||||
hour = dTime.getHours(),
|
||||
minute = Math.floor(dTime.getMinutes() / tp1.minuteStep) * tp1.minuteStep;
|
||||
|
||||
if (hour > 12) {
|
||||
hour = hour - 12;
|
||||
}
|
||||
|
||||
expect(tp1.hour).toBe(hour);
|
||||
expect(tp1.minute).toBe(minute);
|
||||
});
|
||||
|
||||
it('should not override time with current time if value is already set', function() {
|
||||
$('body').append('<div id="timepickerCustom"><input id="timepickerCustomInput" type="text" value="12:15 AM" /></div>');
|
||||
var $customInput = $('#timepickerCustomInput').timepicker(),
|
||||
tpCustom = $customInput.data('timepicker');
|
||||
|
||||
expect($customInput.val()).toBe('12:15 AM');
|
||||
|
||||
tpCustom.remove();
|
||||
$('#timepickerCustom').remove();
|
||||
});
|
||||
|
||||
it('should have no value if defaultTime is set to false', function() {
|
||||
expect($input2.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should be able to set default time with config option', function() {
|
||||
expect(tp3.getTime()).toBe('13:25:15');
|
||||
});
|
||||
|
||||
it('should update the element and widget with the setTime method', function() {
|
||||
tp2.setTime('09:15:20 AM');
|
||||
|
||||
expect(tp2.hour).toBe(9);
|
||||
expect(tp2.minute).toBe(15);
|
||||
expect(tp2.second).toBe(20);
|
||||
expect(tp2.meridian).toBe('AM');
|
||||
expect($input2.val()).toBe('09:15:20 AM');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('09');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('15');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('20');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
|
||||
});
|
||||
|
||||
it('should be able to format time values into a string', function() {
|
||||
expect(tp2.formatTime(3, 15, 45, 'PM')).toBe('03:15:45 PM');
|
||||
});
|
||||
|
||||
it('should be able get & set the pickers time', function() {
|
||||
tp3.setTime('23:15:20');
|
||||
expect(tp3.getTime()).toBe('23:15:20');
|
||||
});
|
||||
|
||||
it('should update picker on blur', function() {
|
||||
$input1.val('10:25 AM');
|
||||
expect(tp1.getTime()).not.toBe('10:25 AM');
|
||||
$input1.trigger('blur');
|
||||
expect(tp1.getTime()).toBe('10:25 AM');
|
||||
});
|
||||
|
||||
it('should update element with updateElement method', function() {
|
||||
tp1.hour = 10;
|
||||
tp1.minute = 30;
|
||||
tp1.meridian = 'PM';
|
||||
tp1.updateElement();
|
||||
expect($input1.val()).toBe('10:30 PM');
|
||||
});
|
||||
|
||||
it('should update widget with updateWidget method', function() {
|
||||
tp2.hour = 10;
|
||||
tp2.minute = 30;
|
||||
tp2.second = 15;
|
||||
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).not.toBe('15');
|
||||
|
||||
tp2.updateWidget();
|
||||
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
|
||||
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('15');
|
||||
});
|
||||
|
||||
it('should update picker with updateFromElementVal method', function() {
|
||||
tp1.hour = 12;
|
||||
tp1.minute = 12;
|
||||
tp1.meridian = 'PM';
|
||||
tp1.update();
|
||||
|
||||
$input1.val('10:30 AM');
|
||||
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).not.toBe('AM');
|
||||
expect(tp1.hour).not.toBe(10);
|
||||
expect(tp1.minute).not.toBe(30);
|
||||
expect(tp1.meridian).not.toBe('AM');
|
||||
|
||||
tp1.updateFromElementVal();
|
||||
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
|
||||
expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
|
||||
expect(tp1.hour).toBe(10);
|
||||
expect(tp1.minute).toBe(30);
|
||||
expect(tp1.meridian).toBe('AM');
|
||||
});
|
||||
|
||||
it('should update picker with updateFromWidgetInputs method', function() {
|
||||
tp1.hour = 12;
|
||||
tp1.minute = 12;
|
||||
tp1.meridian = 'PM';
|
||||
tp1.update();
|
||||
|
||||
tp1.$widget.find('.bootstrap-timepicker-hour').val(10);
|
||||
tp1.$widget.find('.bootstrap-timepicker-minute').val(30);
|
||||
tp1.$widget.find('.bootstrap-timepicker-meridian').val('AM');
|
||||
|
||||
expect(tp1.hour).not.toBe(10);
|
||||
expect(tp1.minute).not.toBe(30);
|
||||
expect(tp1.meridian).not.toBe('AM');
|
||||
expect($input1.val()).not.toBe('10:30 AM');
|
||||
|
||||
tp1.updateFromWidgetInputs();
|
||||
|
||||
expect(tp1.hour).toBe(10);
|
||||
expect(tp1.minute).toBe(30);
|
||||
expect(tp1.meridian).toBe('AM');
|
||||
expect($input1.val()).toBe('10:30 AM');
|
||||
});
|
||||
|
||||
it('should increment hours with incrementHour method', function() {
|
||||
tp1.hour = 9;
|
||||
tp1.incrementHour();
|
||||
expect(tp1.hour).toBe(10);
|
||||
});
|
||||
|
||||
it('should decrement hours with decrementHour method', function() {
|
||||
tp1.hour = 9;
|
||||
tp1.decrementHour();
|
||||
expect(tp1.hour).toBe(8);
|
||||
});
|
||||
|
||||
it('should toggle meridian if hour goes past 12', function() {
|
||||
$input1.val('11:00 AM');
|
||||
tp1.updateFromElementVal();
|
||||
tp1.incrementHour();
|
||||
|
||||
expect(tp1.hour).toBe(12);
|
||||
expect(tp1.minute).toBe(0);
|
||||
expect(tp1.meridian).toBe('PM');
|
||||
});
|
||||
|
||||
it('should toggle meridian if hour goes below 1', function() {
|
||||
$input1.val('11:00 AM');
|
||||
tp1.updateFromElementVal();
|
||||
tp1.incrementHour();
|
||||
|
||||
expect(tp1.hour).toBe(12);
|
||||
expect(tp1.minute).toBe(0);
|
||||
expect(tp1.meridian).toBe('PM');
|
||||
});
|
||||
|
||||
it('should set hour to 1 if hour increments on 12 for 12h clock', function() {
|
||||
$input1.val('11:15 PM');
|
||||
tp1.updateFromElementVal();
|
||||
tp1.incrementHour();
|
||||
tp1.incrementHour();
|
||||
|
||||
expect(tp1.getTime()).toBe('01:15 AM');
|
||||
});
|
||||
|
||||
it('should set hour to 0 if hour increments on 23 for 24h clock', function() {
|
||||
$input3.val('22:15:30');
|
||||
tp3.updateFromElementVal();
|
||||
tp3.incrementHour();
|
||||
tp3.incrementHour();
|
||||
|
||||
expect(tp3.hour).toBe(0);
|
||||
expect(tp3.minute).toBe(15);
|
||||
expect(tp3.second).toBe(30);
|
||||
});
|
||||
|
||||
it('should increment minutes with incrementMinute method', function() {
|
||||
tp1.minute = 10;
|
||||
tp1.incrementMinute();
|
||||
|
||||
expect(tp1.minute).toBe(15);
|
||||
|
||||
tp2.minute = 0;
|
||||
tp2.incrementMinute();
|
||||
|
||||
expect(tp2.minute).toBe(30);
|
||||
});
|
||||
|
||||
it('should decrement minutes with decrementMinute method', function() {
|
||||
tp1.hour = 11;
|
||||
tp1.minute = 0;
|
||||
tp1.decrementMinute();
|
||||
|
||||
expect(tp1.hour).toBe(10);
|
||||
expect(tp1.minute).toBe(45);
|
||||
|
||||
tp2.hour = 11;
|
||||
tp2.minute = 0;
|
||||
tp2.decrementMinute();
|
||||
|
||||
expect(tp2.hour).toBe(10);
|
||||
expect(tp2.minute).toBe(30);
|
||||
});
|
||||
|
||||
|
||||
it('should increment hour if minutes increment past 59', function() {
|
||||
$input1.val('11:55 AM');
|
||||
tp1.updateFromElementVal();
|
||||
tp1.incrementMinute();
|
||||
tp1.update();
|
||||
|
||||
expect(tp1.getTime()).toBe('12:00 PM');
|
||||
});
|
||||
|
||||
it('should toggle meridian with toggleMeridian method', function() {
|
||||
tp1.meridian = 'PM';
|
||||
tp1.toggleMeridian();
|
||||
|
||||
expect(tp1.meridian).toBe('AM');
|
||||
});
|
||||
|
||||
it('should increment seconds with incrementSecond method', function() {
|
||||
tp1.second = 0;
|
||||
tp1.incrementSecond();
|
||||
|
||||
expect(tp1.second).toBe(15);
|
||||
|
||||
tp2.second = 0;
|
||||
tp2.incrementSecond();
|
||||
|
||||
expect(tp2.second).toBe(30);
|
||||
});
|
||||
|
||||
it('should decrement seconds with decrementSecond method', function() {
|
||||
tp2.hour = 11;
|
||||
tp2.minute = 0;
|
||||
tp2.second = 0;
|
||||
tp2.decrementSecond();
|
||||
|
||||
expect(tp2.minute).toBe(59);
|
||||
expect(tp2.second).toBe(30);
|
||||
});
|
||||
|
||||
|
||||
it('should increment minute by 1 if seconds increment past 59', function() {
|
||||
$input2.val('11:55:30 AM');
|
||||
tp2.updateFromElementVal();
|
||||
tp2.incrementSecond();
|
||||
tp2.update();
|
||||
|
||||
expect(tp2.getTime()).toBe('11:56:00 AM');
|
||||
});
|
||||
|
||||
it('should not have any remaining events if remove is called', function() {
|
||||
var hideEvents = 0;
|
||||
|
||||
$input1.on('hide.timepicker', function() {
|
||||
hideEvents++;
|
||||
});
|
||||
|
||||
$input1.parents('div').find('.add-on').trigger('click');
|
||||
$('body').trigger('mousedown');
|
||||
|
||||
expect(hideEvents).toBe(1);
|
||||
|
||||
tp1.remove();
|
||||
tp2.remove();
|
||||
tp3.remove();
|
||||
|
||||
$('body').trigger('click');
|
||||
expect(hideEvents).toBe(1);
|
||||
});
|
||||
|
||||
it('should not have the widget in the DOM if remove method is called', function() {
|
||||
expect($('body')).toContain('.bootstrap-timepicker-widget');
|
||||
tp1.remove();
|
||||
tp2.remove();
|
||||
tp3.remove();
|
||||
expect($('body')).not.toContain('.bootstrap-timepicker-widget');
|
||||
});
|
||||
|
||||
it('should be able to set time from a script', function() {
|
||||
$input1.timepicker('setTime', '12:35 PM');
|
||||
tp1.update();
|
||||
expect(tp1.getTime()).toBe('12:35 PM');
|
||||
});
|
||||
|
||||
it('should be able to opened from script', function() {
|
||||
expect(tp1.isOpen).toBe(false);
|
||||
$input1.timepicker('showWidget');
|
||||
expect(tp1.isOpen).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="input-append bootstrap-timepicker">
|
||||
<input id="timepicker1" type="text" class="input-small">
|
||||
<span class="add-on"><i class="icon-time"></i></span>
|
||||
</div>
|
||||
<div class="input-append bootstrap-timepicker">
|
||||
<input id="timepicker2" type="text" class="input-small">
|
||||
<span class="add-on"><i class="icon-time"></i></span>
|
||||
</div>
|
||||
<div class="bootstrap-timepicker">
|
||||
<input id="timepicker3" type="text" />
|
||||
</div>
|
||||
<div class="bootstrap-timepicker">
|
||||
<input id="timepicker4" type="text" />
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
var readFixtures = function() {
|
||||
return jasmine.getFixtures().proxyCallTo_('read', arguments)
|
||||
}
|
||||
|
||||
var preloadFixtures = function() {
|
||||
jasmine.getFixtures().proxyCallTo_('preload', arguments)
|
||||
}
|
||||
|
||||
var loadFixtures = function() {
|
||||
jasmine.getFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
var appendLoadFixtures = function() {
|
||||
jasmine.getFixtures().proxyCallTo_('appendLoad', arguments)
|
||||
}
|
||||
|
||||
var setFixtures = function(html) {
|
||||
jasmine.getFixtures().proxyCallTo_('set', arguments)
|
||||
}
|
||||
|
||||
var appendSetFixtures = function() {
|
||||
jasmine.getFixtures().proxyCallTo_('appendSet', arguments)
|
||||
}
|
||||
|
||||
var sandbox = function(attributes) {
|
||||
return jasmine.getFixtures().sandbox(attributes)
|
||||
}
|
||||
|
||||
var spyOnEvent = function(selector, eventName) {
|
||||
return jasmine.JQuery.events.spyOn(selector, eventName)
|
||||
}
|
||||
|
||||
var preloadStyleFixtures = function() {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('preload', arguments)
|
||||
}
|
||||
|
||||
var loadStyleFixtures = function() {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
var appendLoadStyleFixtures = function() {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments)
|
||||
}
|
||||
|
||||
var setStyleFixtures = function(html) {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('set', arguments)
|
||||
}
|
||||
|
||||
var appendSetStyleFixtures = function(html) {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments)
|
||||
}
|
||||
|
||||
var loadJSONFixtures = function() {
|
||||
return jasmine.getJSONFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
var getJSONFixture = function(url) {
|
||||
return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url]
|
||||
}
|
||||
|
||||
jasmine.spiedEventsKey = function (selector, eventName) {
|
||||
return [$(selector).selector, eventName].toString()
|
||||
}
|
||||
|
||||
jasmine.getFixtures = function() {
|
||||
return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures()
|
||||
}
|
||||
|
||||
jasmine.getStyleFixtures = function() {
|
||||
return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures()
|
||||
}
|
||||
|
||||
jasmine.Fixtures = function() {
|
||||
this.containerId = 'jasmine-fixtures'
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesPath = 'spec/js/fixtures'
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.set = function(html) {
|
||||
this.cleanUp()
|
||||
this.createContainer_(html)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.appendSet= function(html) {
|
||||
this.addToContainer_(html)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.preload = function() {
|
||||
this.read.apply(this, arguments)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.load = function() {
|
||||
this.cleanUp()
|
||||
this.createContainer_(this.read.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.appendLoad = function() {
|
||||
this.addToContainer_(this.read.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.read = function() {
|
||||
var htmlChunks = []
|
||||
|
||||
var fixtureUrls = arguments
|
||||
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
|
||||
htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex]))
|
||||
}
|
||||
|
||||
return htmlChunks.join('')
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.clearCache = function() {
|
||||
this.fixturesCache_ = {}
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.cleanUp = function() {
|
||||
$('#' + this.containerId).remove()
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.sandbox = function(attributes) {
|
||||
var attributesToSet = attributes || {}
|
||||
return $('<div id="sandbox" />').attr(attributesToSet)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.createContainer_ = function(html) {
|
||||
var container
|
||||
if(html instanceof $) {
|
||||
container = $('<div id="' + this.containerId + '" />')
|
||||
container.html(html)
|
||||
} else {
|
||||
container = '<div id="' + this.containerId + '">' + html + '</div>'
|
||||
}
|
||||
$(document.body).append(container)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.addToContainer_ = function(html){
|
||||
var container = $(document.body).find('#'+this.containerId).append(html)
|
||||
if(!container.length){
|
||||
this.createContainer_(html)
|
||||
}
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.getFixtureHtml_ = function(url) {
|
||||
if (typeof this.fixturesCache_[url] === 'undefined') {
|
||||
this.loadFixtureIntoCache_(url)
|
||||
}
|
||||
return this.fixturesCache_[url]
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
|
||||
var url = this.makeFixtureUrl_(relativeUrl)
|
||||
var request = $.ajax({
|
||||
type: "GET",
|
||||
url: url + "?" + new Date().getTime(),
|
||||
async: false
|
||||
})
|
||||
this.fixturesCache_[relativeUrl] = request.responseText
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.makeFixtureUrl_ = function(relativeUrl){
|
||||
return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
|
||||
return this[methodName].apply(this, passedArguments)
|
||||
}
|
||||
|
||||
|
||||
jasmine.StyleFixtures = function() {
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesNodes_ = []
|
||||
this.fixturesPath = 'spec/javascripts/fixtures'
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.set = function(css) {
|
||||
this.cleanUp()
|
||||
this.createStyle_(css)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.appendSet = function(css) {
|
||||
this.createStyle_(css)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.preload = function() {
|
||||
this.read_.apply(this, arguments)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.load = function() {
|
||||
this.cleanUp()
|
||||
this.createStyle_(this.read_.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.appendLoad = function() {
|
||||
this.createStyle_(this.read_.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.cleanUp = function() {
|
||||
while(this.fixturesNodes_.length) {
|
||||
this.fixturesNodes_.pop().remove()
|
||||
}
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.createStyle_ = function(html) {
|
||||
var styleText = $('<div></div>').html(html).text(),
|
||||
style = $('<style>' + styleText + '</style>')
|
||||
|
||||
this.fixturesNodes_.push(style)
|
||||
|
||||
$('head').append(style)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache
|
||||
|
||||
jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read
|
||||
|
||||
jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_
|
||||
|
||||
jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_
|
||||
|
||||
jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_
|
||||
|
||||
jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_
|
||||
|
||||
jasmine.getJSONFixtures = function() {
|
||||
return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures()
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures = function() {
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesPath = 'spec/javascripts/fixtures/json'
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.load = function() {
|
||||
this.read.apply(this, arguments)
|
||||
return this.fixturesCache_
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.read = function() {
|
||||
var fixtureUrls = arguments
|
||||
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
|
||||
this.getFixtureData_(fixtureUrls[urlIndex])
|
||||
}
|
||||
return this.fixturesCache_
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.clearCache = function() {
|
||||
this.fixturesCache_ = {}
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.getFixtureData_ = function(url) {
|
||||
this.loadFixtureIntoCache_(url)
|
||||
return this.fixturesCache_[url]
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
|
||||
var self = this
|
||||
var url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
|
||||
$.ajax({
|
||||
async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
url: url,
|
||||
success: function(data) {
|
||||
self.fixturesCache_[relativeUrl] = data
|
||||
},
|
||||
fail: function(jqXHR, status, errorThrown) {
|
||||
throw Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
|
||||
return this[methodName].apply(this, passedArguments)
|
||||
}
|
||||
|
||||
jasmine.JQuery = function() {}
|
||||
|
||||
jasmine.JQuery.browserTagCaseIndependentHtml = function(html) {
|
||||
return $('<div/>').append(html).html()
|
||||
}
|
||||
|
||||
jasmine.JQuery.elementToString = function(element) {
|
||||
var domEl = $(element).get(0)
|
||||
if (domEl == undefined || domEl.cloneNode)
|
||||
return $('<div />').append($(element).clone()).html()
|
||||
else
|
||||
return element.toString()
|
||||
}
|
||||
|
||||
jasmine.JQuery.matchersClass = {}
|
||||
|
||||
!function(namespace) {
|
||||
var data = {
|
||||
spiedEvents: {},
|
||||
handlers: []
|
||||
}
|
||||
|
||||
namespace.events = {
|
||||
spyOn: function(selector, eventName) {
|
||||
var handler = function(e) {
|
||||
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = e
|
||||
}
|
||||
$(selector).bind(eventName, handler)
|
||||
data.handlers.push(handler)
|
||||
return {
|
||||
selector: selector,
|
||||
eventName: eventName,
|
||||
handler: handler,
|
||||
reset: function(){
|
||||
delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
wasTriggered: function(selector, eventName) {
|
||||
return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)])
|
||||
},
|
||||
|
||||
wasPrevented: function(selector, eventName) {
|
||||
var e;
|
||||
return (e = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) && e.isDefaultPrevented()
|
||||
},
|
||||
|
||||
cleanUp: function() {
|
||||
data.spiedEvents = {}
|
||||
data.handlers = []
|
||||
}
|
||||
}
|
||||
}(jasmine.JQuery)
|
||||
|
||||
!function(){
|
||||
var jQueryMatchers = {
|
||||
toHaveClass: function(className) {
|
||||
return this.actual.hasClass(className)
|
||||
},
|
||||
|
||||
toHaveCss: function(css){
|
||||
for (var prop in css){
|
||||
if (this.actual.css(prop) !== css[prop]) return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
toBeVisible: function() {
|
||||
return this.actual.is(':visible')
|
||||
},
|
||||
|
||||
toBeHidden: function() {
|
||||
return this.actual.is(':hidden')
|
||||
},
|
||||
|
||||
toBeSelected: function() {
|
||||
return this.actual.is(':selected')
|
||||
},
|
||||
|
||||
toBeChecked: function() {
|
||||
return this.actual.is(':checked')
|
||||
},
|
||||
|
||||
toBeEmpty: function() {
|
||||
return this.actual.is(':empty')
|
||||
},
|
||||
|
||||
toExist: function() {
|
||||
return $(document).find(this.actual).length
|
||||
},
|
||||
|
||||
toHaveLength: function(length) {
|
||||
return this.actual.length === length
|
||||
},
|
||||
|
||||
toHaveAttr: function(attributeName, expectedAttributeValue) {
|
||||
return hasProperty(this.actual.attr(attributeName), expectedAttributeValue)
|
||||
},
|
||||
|
||||
toHaveProp: function(propertyName, expectedPropertyValue) {
|
||||
return hasProperty(this.actual.prop(propertyName), expectedPropertyValue)
|
||||
},
|
||||
|
||||
toHaveId: function(id) {
|
||||
return this.actual.attr('id') == id
|
||||
},
|
||||
|
||||
toHaveHtml: function(html) {
|
||||
return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html)
|
||||
},
|
||||
|
||||
toContainHtml: function(html){
|
||||
var actualHtml = this.actual.html()
|
||||
var expectedHtml = jasmine.JQuery.browserTagCaseIndependentHtml(html)
|
||||
return (actualHtml.indexOf(expectedHtml) >= 0)
|
||||
},
|
||||
|
||||
toHaveText: function(text) {
|
||||
var trimmedText = $.trim(this.actual.text())
|
||||
if (text && $.isFunction(text.test)) {
|
||||
return text.test(trimmedText)
|
||||
} else {
|
||||
return trimmedText == text
|
||||
}
|
||||
},
|
||||
|
||||
toHaveValue: function(value) {
|
||||
return this.actual.val() == value
|
||||
},
|
||||
|
||||
toHaveData: function(key, expectedValue) {
|
||||
return hasProperty(this.actual.data(key), expectedValue)
|
||||
},
|
||||
|
||||
toBe: function(selector) {
|
||||
return this.actual.is(selector)
|
||||
},
|
||||
|
||||
toContain: function(selector) {
|
||||
return this.actual.find(selector).length
|
||||
},
|
||||
|
||||
toBeDisabled: function(selector){
|
||||
return this.actual.is(':disabled')
|
||||
},
|
||||
|
||||
toBeFocused: function(selector) {
|
||||
return this.actual[0] === this.actual[0].ownerDocument.activeElement
|
||||
},
|
||||
|
||||
toHandle: function(event) {
|
||||
|
||||
var events = $._data(this.actual.get(0), "events")
|
||||
|
||||
if(!events || !event || typeof event !== "string") {
|
||||
return false
|
||||
}
|
||||
|
||||
var namespaces = event.split(".")
|
||||
var eventType = namespaces.shift()
|
||||
var sortedNamespaces = namespaces.slice(0).sort()
|
||||
var namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)")
|
||||
|
||||
if(events[eventType] && namespaces.length) {
|
||||
for(var i = 0; i < events[eventType].length; i++) {
|
||||
var namespace = events[eventType][i].namespace
|
||||
if(namespaceRegExp.test(namespace)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return events[eventType] && events[eventType].length > 0
|
||||
}
|
||||
},
|
||||
|
||||
// tests the existence of a specific event binding + handler
|
||||
toHandleWith: function(eventName, eventHandler) {
|
||||
var stack = $._data(this.actual.get(0), "events")[eventName]
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
if (stack[i].handler == eventHandler) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var hasProperty = function(actualValue, expectedValue) {
|
||||
if (expectedValue === undefined) return actualValue !== undefined
|
||||
return actualValue == expectedValue
|
||||
}
|
||||
|
||||
var bindMatcher = function(methodName) {
|
||||
var builtInMatcher = jasmine.Matchers.prototype[methodName]
|
||||
|
||||
jasmine.JQuery.matchersClass[methodName] = function() {
|
||||
if (this.actual
|
||||
&& (this.actual instanceof $
|
||||
|| jasmine.isDomNode(this.actual))) {
|
||||
this.actual = $(this.actual)
|
||||
var result = jQueryMatchers[methodName].apply(this, arguments)
|
||||
var element
|
||||
if (this.actual.get && (element = this.actual.get()[0]) && !$.isWindow(element) && element.tagName !== "HTML")
|
||||
this.actual = jasmine.JQuery.elementToString(this.actual)
|
||||
return result
|
||||
}
|
||||
|
||||
if (builtInMatcher) {
|
||||
return builtInMatcher.apply(this, arguments)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for(var methodName in jQueryMatchers) {
|
||||
bindMatcher(methodName)
|
||||
}
|
||||
}()
|
||||
|
||||
beforeEach(function() {
|
||||
this.addMatchers(jasmine.JQuery.matchersClass)
|
||||
this.addMatchers({
|
||||
toHaveBeenTriggeredOn: function(selector) {
|
||||
this.message = function() {
|
||||
return [
|
||||
"Expected event " + this.actual + " to have been triggered on " + selector,
|
||||
"Expected event " + this.actual + " not to have been triggered on " + selector
|
||||
]
|
||||
}
|
||||
return jasmine.JQuery.events.wasTriggered(selector, this.actual)
|
||||
}
|
||||
})
|
||||
this.addMatchers({
|
||||
toHaveBeenTriggered: function(){
|
||||
var eventName = this.actual.eventName,
|
||||
selector = this.actual.selector
|
||||
this.message = function() {
|
||||
return [
|
||||
"Expected event " + eventName + " to have been triggered on " + selector,
|
||||
"Expected event " + eventName + " not to have been triggered on " + selector
|
||||
]
|
||||
}
|
||||
return jasmine.JQuery.events.wasTriggered(selector, eventName)
|
||||
}
|
||||
})
|
||||
this.addMatchers({
|
||||
toHaveBeenPreventedOn: function(selector) {
|
||||
this.message = function() {
|
||||
return [
|
||||
"Expected event " + this.actual + " to have been prevented on " + selector,
|
||||
"Expected event " + this.actual + " not to have been prevented on " + selector
|
||||
]
|
||||
}
|
||||
return jasmine.JQuery.events.wasPrevented(selector, this.actual)
|
||||
}
|
||||
})
|
||||
this.addMatchers({
|
||||
toHaveBeenPrevented: function() {
|
||||
var eventName = this.actual.eventName,
|
||||
selector = this.actual.selector
|
||||
this.message = function() {
|
||||
return [
|
||||
"Expected event " + eventName + " to have been prevented on " + selector,
|
||||
"Expected event " + eventName + " not to have been prevented on " + selector
|
||||
]
|
||||
}
|
||||
return jasmine.JQuery.events.wasPrevented(selector, eventName)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
jasmine.getFixtures().cleanUp()
|
||||
jasmine.getStyleFixtures().cleanUp()
|
||||
jasmine.JQuery.events.cleanUp()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user