From 1aa3c9f0cb17e54a4af9ee97957f92bbe90af279 Mon Sep 17 00:00:00 2001 From: Joris de Wit Date: Sun, 29 Apr 2012 10:32:59 -0700 Subject: [PATCH] upload project --- README.md | 9 + js/bootstrap-timepicker.js | 368 +++++++++++++++++++++++++++++++++++++ less/timepicker.less | 69 +++++++ 3 files changed, 446 insertions(+) create mode 100644 README.md create mode 100644 js/bootstrap-timepicker.js create mode 100644 less/timepicker.less diff --git a/README.md b/README.md new file mode 100644 index 00000000..0c7a5ed9 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +Timepicker for Twitter Bootstrap 2.x +------------------------------------ + +A simple timepicker component for Twitter Bootstrap. + +Documentation +============= + +Read the documentation. diff --git a/js/bootstrap-timepicker.js b/js/bootstrap-timepicker.js new file mode 100644 index 00000000..a76d2abb --- /dev/null +++ b/js/bootstrap-timepicker.js @@ -0,0 +1,368 @@ +/* ========================================================= + * bootstrap-timepicker.js + * http://www.github.com/jdewit/bootstrap-timepicker + * ========================================================= + * Copyright 2012 Joris de Wit, Stefan Petre, and Andrew Rowls + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= */ + +!function( $ ) { + + // Picker object + var Timepicker = function(element, options){ + this.element = $(element); + this.step = options.step||this.element.data('time-step')||1; + this.picker = $('') + .appendTo('body') + .on({ + click: $.proxy(this.click, this), + mousedown: $.proxy(this.mousedown, this) + }); + this.isInput = this.element.is('input'); + this.component = this.element.is('.time') ? this.element.find('.add-on') : false; + + if (this.component && this.component.length === 0) { + this.component = false; + } + + if (this.isInput) { + this.element.on({ + focus: $.proxy(this.show, this), + blur: $.proxy(this._hide, this), + keyup: $.proxy(this.update, this), + keydown: $.proxy(this.keydown, this) + }); + } else { + if (this.component){ + this.component.on('click', $.proxy(this.show, this)); + var element = this.element.find('input'); + element.on({ + blur: $.proxy(this._hide, this) + }) + } else { + this.element.on('click', $.proxy(this.show, this)); + } + } + + this.setDefaultTime(options.defaultTime||this.element.data('time-default-time')); + + this.update(); + }; + + Timepicker.prototype = { + constructor: Timepicker, + + show: function(e) { + this.picker.show(); + this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); + this.place(); + $(window).on('resize', $.proxy(this.place, this)); + if (e ) { + e.stopPropagation(); + e.preventDefault(); + } + if (!this.isInput) { + $(document).on('mousedown', $.proxy(this.hide, this)); + } + this.element.trigger({ + type: 'show', + time: this.time + }); + }, + + _hide: function(e){ + // When going from the input to the picker, IE handles the blur/click + // events differently than other browsers, in such a way that the blur + // event triggers a hide before the click event can stop propagation. + if ($.browser.msie) { + var t = this, args = arguments; + + function cancel_hide(){ + clearTimeout(hide_timeout); + e.target.focus(); + t.picker.off('click', cancel_hide); + } + + function do_hide(){ + t.hide.apply(t, args); + t.picker.off('click', cancel_hide); + } + + this.picker.on('click', cancel_hide); + var hide_timeout = setTimeout(do_hide, 100); + } else { + return this.hide.apply(this, arguments); + } + }, + + hide: function(e){ + this.picker.hide(); + $(window).off('resize', this.place); + if (!this.isInput) { + $(document).off('mousedown', this.hide); + } + if (e && e.currentTarget.value) { + this.setValue(); + } + this.element.trigger({ + type: 'hide', + time: this.getTime() + }); + }, + + setValue: function(input) { + if (!this.isInput) { + if (this.component){ + this.element.find('input').prop('value', input); + } + this.element.data('time', input); + } else { + this.element.prop('value', input); + } + $('.timepicker td#timepicker-hour').text(this.hour); + $('.timepicker td#timepicker-minute').text(this.minute < 10 ? '0' + this.minute : this.minute); + $('.timepicker td#timepicker-meridian').text(this.meridian); + }, + + setValues: function(time) { + var meridian = time.replace('/am/i', ''); + if (!meridian) { + meridian = time.replace('/pm/i', ''); + } + var timeArray = time.split(':'); + + this.meridian = meridian; + this.hour = timeArray[0]; + this.minute = timeArray[1]; + }, + + setDefaultTime: function(defaultTime){ + if (!defaultTime || defaultTime == 'current') { + var dTime = new Date(); + var hours = dTime.getHours(); + var minutes = Math.floor(dTime.getMinutes() / this.step) * this.step; + + var meridian = "am"; + if (hours > 12) { + hours = hours - 12; + meridian = "pm"; + } else { + meridian = "am"; + } + + this.hour = hours; + this.minute = minutes; + this.meridian = meridian; + + this.update(); + } else { + this.setValues(defaultTime); + } + }, + + place: function(){ + var offset = this.component ? this.component.offset() : this.element.offset(); + this.picker.css({ + top: offset.top + this.height, + left: offset.left + }); + }, + + formatTime: function(hour, minute, meridian) { + hour = hour < 10 ? '0' + hour : hour; + minute = minute < 10 ? '0' + minute : minute; + + return hour + ':' + minute + ' ' + meridian; + }, + + getTime: function() { + return this.formatTime(this.hour, this.minute, this.meridian); + }, + + update: function(){ + var time = this.getTime(); + this.setValue(time); + + this.element.trigger({ + type: 'changeTime', + time: time + }); + }, + + click: function(e) { + e.stopPropagation(); + e.preventDefault(); + var action = $(e.target).closest('a').data('action'); + if (action) { + switch(action) { + case 'incrementHour': + this.incrementHour(); + break; + case 'decrementHour': + this.decrementHour(); + break; + case 'incrementMinute': + this.incrementMinute(); + break; + case 'decrementMinute': + this.decrementMinute(); + break; + case 'toggleMeridian': + this.toggleMeridian(); + break; + } + } + + }, + + mousedown: function(e){ + e.stopPropagation(); + e.preventDefault(); + }, + + incrementHour: function(){ + if (this.hour === 12) { + this.hour = 1; + this.toggleMeridian(); + } else { + this.hour = this.hour + 1; + } + this.update(); + }, + + decrementHour: function(){ + if (this.hour === 1) { + this.hour = 12; + this.toggleMeridian(); + } else { + this.hour = this.hour - 1; + } + + this.update(); + }, + + incrementMinute: function(){ + var newVal = this.minute + this.step; + if (newVal > 59) { + this.incrementHour(); + this.minute = newVal - 60; + } else { + this.minute = newVal; + } + + this.update(); + }, + + decrementMinute: function() { + var newVal = this.minute - this.step; + if (newVal < 0) { + this.decrementHour(); + this.minute = newVal + 60; + } else { + this.minute = newVal; + } + + this.update(); + }, + + toggleMeridian: function() { + if (this.meridian == 'am') { + this.meridian = 'pm'; + } else { + this.meridian = 'am'; + } + + this.update(); + }, + + keydown: function(e){ + if (this.picker.is(':not(:visible)')){ + if (e.keyCode == 27) // allow escape to hide and re-show picker + this.show(); + return; + } + var timeChanged = false, + dir, day, hour; + switch(e.keyCode){ + case 27: // escape + this.hide(); + e.preventDefault(); + break; + case 37: // left + case 39: // right + case 38: // up + case 40: // down + case 13: // enter + this.hide(); + e.preventDefault(); + break; + } + if (timeChanged){ + this.element.trigger({ + type: 'changeTime', + time: this.time + }); + var element; + if (this.isInput) { + element = this.element; + } else if (this.component){ + element = this.element.find('input'); + } + if (element) { + element.change(); + } + } + }, + }; + + $.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)))); + } + if (typeof option == 'string') data[option].apply(data, args); + }); + }; + + $.fn.timepicker.Constructor = Timepicker; +}( window.jQuery ) + diff --git a/less/timepicker.less b/less/timepicker.less new file mode 100644 index 00000000..da8e30eb --- /dev/null +++ b/less/timepicker.less @@ -0,0 +1,69 @@ +/*! + * Timepicker for Bootstrap + * + * Copyright 2012 Joris de Wit, Stefan Petre, Andrew Rowls + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +.timepicker { + top: 0; + left: 0; + padding: 4px; + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + + &:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; + } + + &:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; + } + + table { + width: 100%; + margin: 0; + } + + td, th { + text-align: center; + height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + + td.separator { + width: 1px; + } + + a { + border: 1px transparent solid; + + &:hover { + background-color: #eee; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border-color: #ddd; + } + } +}