Initial commit

This commit is contained in:
Khanh Ngo
2015-12-13 16:34:12 +07:00
commit 2dac8205f6
3113 changed files with 514935 additions and 0 deletions

View File

@ -0,0 +1,12 @@
Clockface changelog
=============================
Version 1.0.1 Aug 27, 2013
-----------------------------
[enh] show '12' instead of '00' in 12h format (vitalets)
Version 1.0.0 Dec 18, 2012
-----------------------------
Initial release.

View File

@ -0,0 +1,22 @@
Copyright (c) 2012 Vitaliy Potapov
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.

View File

@ -0,0 +1,13 @@
# Clockface
Clock-like timepicker for Twitter Bootstrap.
## Demo, Docs and Download
See **http://vitalets.github.com/clockface**
## Contribution
Your support is appreciated.
Please make pull requests on <code>dev</code> branch. Thank you!
## License
Copyright (c) 2012 Vitaliy Potapov
Licensed under the MIT licenses.

View File

@ -0,0 +1,229 @@
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
line-height: 0;
}
.clearfix:after {
clear: both;
}
.hide-text {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.input-block-level {
display: block;
width: 100%;
min-height: 30px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.clockface {
width: 160px;
padding: 3px;
text-align: center;
/*
.l3 .center span {
vertical-align: middle;
display: inline-block;
.ie7-inline-block();
padding: 0 2px;
}
*/
/*
input {
width: 20px;
margin: 0;
vertical-align: top;
}
a {
text-decoration: none;
padding: 0 3px;
vertical-align: top;
font-size: 0.85em;
.border-radius(3px);
&.am {margin-right: 8px;}
&.active,
&.active:hover {
.buttonBackground(@btnSuccessBackground, spin(@btnSuccessBackground, 20));
}
}
*/
}
.clockface > div {
clear: both;
overflow: auto;
}
.clockface .outer,
.clockface .inner {
width: 22px;
height: 22px;
line-height: 22px;
cursor: default;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.clockface .outer.active,
.clockface .inner.active,
.clockface .outer.active:hover,
.clockface .inner.active:hover {
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.clockface .outer:hover,
.clockface .inner:hover {
background-color: #dcdcdc;
}
.clockface .outer {
color: gray;
font-size: 0.8em;
}
.clockface .outer.active,
.clockface .outer.active:hover {
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #5bb75b;
background-image: -moz-linear-gradient(top, #62c462, #51a351);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
background-image: -webkit-linear-gradient(top, #62c462, #51a351);
background-image: -o-linear-gradient(top, #62c462, #51a351);
background-image: linear-gradient(to bottom, #62c462, #51a351);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
border-color: #51a351 #51a351 #387038;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #51a351;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.clockface .outer.active:hover,
.clockface .outer.active:hover:hover,
.clockface .outer.active:active,
.clockface .outer.active:hover:active,
.clockface .outer.active.active,
.clockface .outer.active:hover.active,
.clockface .outer.active.disabled,
.clockface .outer.active:hover.disabled,
.clockface .outer.active[disabled],
.clockface .outer.active:hover[disabled] {
color: #ffffff;
background-color: #51a351;
*background-color: #499249;
}
.clockface .outer.active:active,
.clockface .outer.active:hover:active,
.clockface .outer.active.active,
.clockface .outer.active:hover.active {
background-color: #408140 \9;
}
.clockface .inner.active,
.clockface .inner.active:hover {
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(to bottom, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #0044cc;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.clockface .inner.active:hover,
.clockface .inner.active:hover:hover,
.clockface .inner.active:active,
.clockface .inner.active:hover:active,
.clockface .inner.active.active,
.clockface .inner.active:hover.active,
.clockface .inner.active.disabled,
.clockface .inner.active:hover.disabled,
.clockface .inner.active[disabled],
.clockface .inner.active:hover[disabled] {
color: #ffffff;
background-color: #0044cc;
*background-color: #003bb3;
}
.clockface .inner.active:active,
.clockface .inner.active:hover:active,
.clockface .inner.active.active,
.clockface .inner.active:hover.active {
background-color: #003399 \9;
}
.clockface .l1 .cell,
.clockface .l5 .cell {
width: 22px;
display: inline-block;
*display: inline;
/* IE7 inline-block hack */
*zoom: 1;
}
.clockface .l1 .outer {
margin-bottom: 3px;
}
.clockface .l5 .outer {
margin-top: 3px;
}
.clockface .l2 .outer,
.clockface .l3 .outer,
.clockface .l4 .outer,
.clockface .l2 .inner,
.clockface .l3 .inner,
.clockface .l4 .inner {
display: inline-block;
*display: inline;
/* IE7 inline-block hack */
*zoom: 1;
vertical-align: middle;
}
.clockface .l2 .left,
.clockface .l3 .left,
.clockface .l4 .left {
float: left;
}
.clockface .l2 .left .outer,
.clockface .l3 .left .outer,
.clockface .l4 .left .outer {
margin-right: 3px;
}
.clockface .l2 .right,
.clockface .l3 .right,
.clockface .l4 .right {
float: right;
}
.clockface .l2 .right .outer,
.clockface .l3 .right .outer,
.clockface .l4 .right .outer {
margin-left: 3px;
}
.clockface .ampm {
font-size: 0.8em;
text-decoration: none;
border-bottom: dashed 1px;
}
.clockface .ampm:focus {
outline: 0;
outline: thin dotted \9;
/* IE6-9 */
}

View File

@ -0,0 +1,609 @@
/**
* Clockface - v1.0.1
* Clockface timepicker for Twitter Bootstrap
*
* Confusion with noon and midnight:
* http://en.wikipedia.org/wiki/12-hour_clock
* Here considered '00:00 am' as midnight and '12:00 pm' as noon.
*
* Author: Vitaliy Potapov
* Project page: http://github.com/vitalets/clockface
* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
**/
(function ($) {
var Clockface = function (element, options) {
this.$element = $(element);
this.options = $.extend({}, $.fn.clockface.defaults, options, this.$element.data());
this.init();
};
Clockface.prototype = {
constructor: Clockface,
init: function () {
//apply template
this.$clockface = $($.fn.clockface.template);
this.$clockface.find('.l1 .cell, .left.cell').html('<div class="outer"></div><div class="inner"></div>');
this.$clockface.find('.l5 .cell, .right.cell').html('<div class="inner"></div><div class="outer"></div>');
this.$clockface.hide();
this.$outer = this.$clockface.find('.outer');
this.$inner = this.$clockface.find('.inner');
this.$ampm = this.$clockface.find('.ampm');
//internal vars
this.ampm = null;
this.hour = null;
this.minute = null;
//click am/pm
this.$ampm.click($.proxy(this.clickAmPm, this));
//click cell
this.$clockface.on('click', '.cell', $.proxy(this.click, this));
this.parseFormat();
this.prepareRegexp();
//set ampm text
this.ampmtext = this.is24 ? {am: '12-23', pm: '0-11'} : {am: 'AM', pm: 'PM'};
this.isInline = this.$element.is('div');
if(this.isInline) {
this.$clockface.addClass('clockface-inline').appendTo(this.$element);
} else {
this.$clockface.addClass('dropdown-menu').appendTo('body');
if(this.options.trigger === 'focus') {
this.$element.on('focus.clockface', $.proxy(function(e) { this.show(); }, this));
}
// Click outside hide it. Register single handler for all clockface widgets
$(document).off('click.clockface').on('click.clockface', $.proxy(function (e) {
var $target = $(e.target);
//click inside some clockface --> do nothing
if ($target.closest('.clockface').length) {
return;
}
//iterate all open clockface and close all except current
$('.clockface-open').each(function(){
if(this === e.target) {
return;
}
$(this).clockface('hide');
});
}, this));
}
//fill minutes once
this.fill('minute');
},
/*
Displays widget with specified value
*/
show: function(value) {
if(this.$clockface.is(':visible')) {
return;
}
if(!this.isInline) {
if(value === undefined) {
value = this.$element.val();
}
this.$element.addClass('clockface-open');
this.$element.on('keydown.clockface', $.proxy(this.keydown, this));
this.place();
$(window).on('resize.clockface', $.proxy(this.place, this));
}
this.$clockface.show();
this.setTime(value);
//trigger shown event
this.$element.triggerHandler('shown.clockface', this.getTime(true));
},
/*
hides widget
*/
hide: function() {
this.$clockface.hide();
if(!this.isInline) {
this.$element.removeClass('clockface-open');
this.$element.off('keydown.clockface');
$(window).off('resize.clockface');
}
//trigger hidden event
this.$element.triggerHandler('hidden.clockface', this.getTime(true));
},
/*
toggles show/hide
*/
toggle: function(value) {
if(this.$clockface.is(':visible')) {
this.hide();
} else {
this.show(value);
}
},
/*
Set time of clockface. Am/pm will be set automatically.
Value can be Date object or string
*/
setTime: function(value) {
var res, hour, minute, ampm = 'am';
//no new value
if(value === undefined) {
//if ampm null, it;s first showw, need to render hours ('am' by default)
if(this.ampm === null) {
this.setAmPm('am');
}
return;
}
//take value from Date object
if(value instanceof Date) {
hour = value.getHours();
minute = value.getMinutes();
}
//parse value from string
if(typeof value === 'string' && value.length) {
res = this.parseTime(value);
//'24' always '0'
if(res.hour === 24) {
res.hour = 0;
}
hour = res.hour;
minute = res.minute;
ampm = res.ampm;
}
//try to set ampm automatically
if(hour > 11 && hour < 24) {
ampm = 'pm';
//for 12h format substract 12 from value
if(!this.is24 && hour > 12) {
hour -= 12;
}
} else if(hour >= 0 && hour < 11) {
//always set am for 24h and for '0' in 12h
if(this.is24 || hour === 0) {
ampm = 'am';
}
//otherwise ampm should be defined in value itself and retrieved when parsing
}
this.setAmPm(ampm);
this.setHour(hour);
this.setMinute(minute);
},
/*
Set ampm and re-fill hours
*/
setAmPm: function(value) {
if(value === this.ampm) {
return;
} else {
this.ampm = value === 'am' ? 'am' : 'pm';
}
//set link's text
this.$ampm.text(this.ampmtext[this.ampm]);
//re-fill and highlight hour
this.fill('hour');
this.highlight('hour');
},
/*
Sets hour value and highlight if possible
*/
setHour: function(value) {
value = parseInt(value, 10);
value = isNaN(value) ? null : value;
if(value < 0 || value > 23) {
value = null;
}
if(value === this.hour) {
return;
} else {
this.hour = value;
}
this.highlight('hour');
},
/*
Sets minute value and highlight
*/
setMinute: function(value) {
value = parseInt(value, 10);
value = isNaN(value) ? null : value;
if(value < 0 || value > 59) {
value = null;
}
if(value === this.minute) {
return;
} else {
this.minute = value;
}
this.highlight('minute');
},
/*
Highlights hour/minute
*/
highlight: function(what) {
var index,
values = this.getValues(what),
value = what === 'minute' ? this.minute : this.hour,
$cells = what === 'minute' ? this.$outer : this.$inner;
$cells.removeClass('active');
//find index of value and highlight if possible
index = $.inArray(value, values);
if(index >= 0) {
$cells.eq(index).addClass('active');
}
},
/*
Fill values around
*/
fill: function(what) {
var values = this.getValues(what),
$cells = what === 'minute' ? this.$outer : this.$inner,
leadZero = what === 'minute';
$cells.each(function(i){
var v = values[i];
if(leadZero && v < 10) {
v = '0' + v;
}
$(this).text(v);
});
},
/*
returns values of hours or minutes, depend on ampm and 24/12 format (0-11, 12-23, 00-55, etc)
param what: 'hour'/'minute'
*/
getValues: function(what) {
var values = [11, 0, 1, 10, 2, 9, 3, 8, 4, 7, 6, 5],
result = values.slice();
//minutes
if(what === 'minute') {
$.each(values, function(i, v) { result[i] = v*5; });
} else {
//hours
if(!this.is24) {
result[1] = 12; //need this to show '12' instead of '00' for 12h am/pm
}
if(this.is24 && this.ampm === 'pm') {
$.each(values, function(i, v) { result[i] = v+12; });
}
}
return result;
},
/*
Click cell handler.
Stores hour/minute and highlights.
On second click deselect value
*/
click: function(e) {
var $target = $(e.target),
value = $target.hasClass('active') ? null : $target.text();
if($target.hasClass('inner')) {
this.setHour(value);
} else {
this.setMinute(value);
}
//update value in input
if(!this.isInline) {
this.$element.val(this.getTime());
}
//trigger pick event
this.$element.triggerHandler('pick.clockface', this.getTime(true));
},
/*
Click handler on ampm link
*/
clickAmPm: function(e) {
e.preventDefault();
//toggle am/pm
this.setAmPm(this.ampm === 'am' ? 'pm' : 'am');
//update value in input
if(!this.isInline && !this.is24) {
this.$element.val(this.getTime());
}
//trigger pick event
this.$element.triggerHandler('pick.clockface', this.getTime(true));
},
/*
Place widget below input
*/
place: function(){
var zIndex = parseInt(this.$element.parents().filter(function() {
return $(this).css('z-index') != 'auto';
}).first().css('z-index'), 10)+10,
offset = this.$element.offset();
this.$clockface.css({
top: offset.top + this.$element.outerHeight(),
left: offset.left,
zIndex: zIndex
});
},
/*
keydown handler (for not inline mode)
*/
keydown: function(e) {
//tab, escape, enter --> hide
if(/^(9|27|13)$/.test(e.which)) {
this.hide();
return;
}
clearTimeout(this.timer);
this.timer = setTimeout($.proxy(function(){
this.setTime(this.$element.val());
}, this), 500);
},
/*
Parse format from options and set this.is24
*/
parseFormat: function() {
var format = this.options.format,
hFormat = 'HH',
mFormat = 'mm';
//hour format
$.each(['HH', 'hh', 'H', 'h'], function(i, f){
if(format.indexOf(f) !== -1) {
hFormat = f;
return false;
}
});
//minute format
$.each(['mm', 'm'], function(i, f){
if(format.indexOf(f) !== -1) {
mFormat = f;
return false;
}
});
//is 24 hour format
this.is24 = hFormat.indexOf('H') !== -1;
this.hFormat = hFormat;
this.mFormat = mFormat;
},
/*
Parse value passed as string or Date object
*/
parseTime: function(value) {
var hour = null,
minute = null,
ampm = 'am',
parts = [], digits;
value = $.trim(value);
//try parse time from string assuming separator exist
if(this.regexpSep) {
parts = value.match(this.regexpSep);
}
if(parts && parts.length) {
hour = parts[1] ? parseInt(parts[1], 10) : null;
minute = parts[2] ? parseInt(parts[2], 10): null;
ampm = (!parts[3] || parts[3].toLowerCase() === 'a') ? 'am' : 'pm';
} else {
//if parse with separator failed, search for 1,4-digit block and process it
//use reversed string to start from end (usefull with full dates)
//see http://stackoverflow.com/questions/141348/what-is-the-best-way-to-parse-a-time-into-a-date-object-from-user-input-in-javas
value = value.split('').reverse().join('').replace(/\s/g, '');
parts = value.match(this.regexpNoSep);
if(parts && parts.length) {
ampm = (!parts[1] || parts[1].toLowerCase() === 'a') ? 'am' : 'pm';
//reverse back
digits = parts[2].split('').reverse().join('');
//use smart analyzing to detect hours and minutes
switch(digits.length) {
case 1:
hour = parseInt(digits, 10); //e.g. '6'
break;
case 2:
hour = parseInt(digits, 10); //e.g. '16'
//if((this.is24 && hour > 24) || (!this.is24 && hour > 12)) { //e.g. 26
if(hour > 24) { //e.g. 26
hour = parseInt(digits[0], 10);
minute = parseInt(digits[1], 10);
}
break;
case 3:
hour = parseInt(digits[0], 10); //e.g. 105
minute = parseInt(digits[1]+digits[2], 10);
if(minute > 59) {
hour = parseInt(digits[0]+digits[1], 10); //e.g. 195
minute = parseInt(digits[2], 10);
if(hour > 24) {
hour = null;
minute = null;
}
}
break;
case 4:
hour = parseInt(digits[0]+digits[1], 10); //e.g. 2006
minute = parseInt(digits[2]+digits[3], 10);
if(hour > 24) {
hour = null;
}
if(minute > 59) {
minute = null;
}
}
}
}
return {hour: hour, minute: minute, ampm: ampm};
},
prepareRegexp: function() {
//take separator from format
var sep = this.options.format.match(/h\s*([^hm]?)\s*m/i); //HH-mm, HH:mm
if(sep && sep.length) {
sep = sep[1];
}
//sep can be null for HH, and '' for HHmm
this.separator = sep;
//parse from string
//use reversed string and regexp to parse 2-digit minutes first
//see http://stackoverflow.com/questions/141348/what-is-the-best-way-to-parse-a-time-into-a-date-object-from-user-input-in-javas
//this.regexp = new RegExp('(a|p)?\\s*((\\d\\d?)' + sep + ')?(\\d\\d?)', 'i');
//regexp, used with separator
this.regexpSep = (this.separator && this.separator.length) ? new RegExp('(\\d\\d?)\\s*\\' + this.separator + '\\s*(\\d?\\d?)\\s*(a|p)?', 'i') : null;
//second regexp applied if previous has no result or separator is empty (to reversed string)
this.regexpNoSep = new RegExp('(a|p)?\\s*(\\d{1,4})', 'i');
},
/*
Returns time as string in specified format
*/
getTime: function(asObject) {
if(asObject === true) {
return {
hour: this.hour,
minute: this.minute,
ampm: this.ampm
};
}
var hour = this.hour !== null ? this.hour + '' : '',
minute = this.minute !== null ? this.minute + '' : '',
result = this.options.format;
if(!hour.length && !minute.length) {
return '';
}
if(this.hFormat.length > 1 && hour.length === 1) {
hour = '0' + hour;
}
if(this.mFormat.length > 1 && minute.length === 1) {
minute = '0' + minute;
}
//delete separator if no minutes
if(!minute.length && this.separator) {
result = result.replace(this.separator, '');
}
result = result.replace(this.hFormat, hour).replace(this.mFormat, minute);
if(!this.is24) {
if(result.indexOf('A') !== -1) {
result = result.replace('A', this.ampm.toUpperCase());
} else {
result = result.replace('a', this.ampm);
}
}
return result;
},
/*
Removes widget and detach events
*/
destroy: function() {
this.hide();
this.$clockface.remove();
if(!this.isInline && this.options.trigger === 'focus') {
this.$element.off('focus.clockface');
}
}
};
$.fn.clockface = function ( option ) {
var d, args = Array.apply(null, arguments);
args.shift();
//getTime returns string (not jQuery onject)
if(option === 'getTime' && this.length && (d = this.eq(0).data('clockface'))) {
return d.getTime.apply(d, args);
}
return this.each(function () {
var $this = $(this),
data = $this.data('clockface'),
options = typeof option == 'object' && option;
if (!data) {
$this.data('clockface', (data = new Clockface(this, options)));
}
if (typeof option == 'string' && typeof data[option] == 'function') {
data[option].apply(data, args);
}
});
};
$.fn.clockface.defaults = {
//see http://momentjs.com/docs/#/displaying/format/
format: 'H:mm',
trigger: 'focus' //focus|manual
};
$.fn.clockface.template = ''+
'<div class="clockface">' +
'<div class="l1">' +
'<div class="cell"></div>' +
'<div class="cell"></div>' +
'<div class="cell"></div>' +
'</div>' +
'<div class="l2">' +
'<div class="cell left"></div>' +
'<div class="cell right"></div>' +
'</div>'+
'<div class="l3">' +
'<div class="cell left"></div>' +
'<div class="cell right"></div>' +
'<div class="center"><a href="#" class="ampm"></a></div>' +
'</div>'+
'<div class="l4">' +
'<div class="cell left"></div>' +
'<div class="cell right"></div>' +
'</div>'+
'<div class="l5">' +
'<div class="cell"></div>' +
'<div class="cell"></div>' +
'<div class="cell"></div>' +
'</div>'+
'</div>';
}(window.jQuery));