Files
SkinbaseNova/public/assets/plugins/pickadate/picker.time.js
2026-02-07 08:23:18 +01:00

763 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*!
* Time picker for pickadate.js v3.3.2
* http://amsul.github.io/pickadate.js/time.htm
*/
/*jshint
debug: true,
devel: true,
browser: true,
asi: true,
unused: true,
boss: true
*/
(function ( factory ) {
// Register as an anonymous module.
if ( typeof define === 'function' && define.amd )
define( ['picker','jquery'], factory )
// Or using browser globals.
else factory( Picker, jQuery )
}(function( Picker, $ ) {
/**
* Globals and constants
*/
var HOURS_IN_DAY = 24,
MINUTES_IN_HOUR = 60,
HOURS_TO_NOON = 12,
MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR
/**
* The time picker constructor
*/
function TimePicker( picker, settings ) {
var clock = this,
elementDataValue = picker.$node.data( 'value' )
clock.settings = settings
// The queue of methods that will be used to build item objects.
clock.queue = {
interval: 'i',
min: 'measure create',
max: 'measure create',
now: 'now create',
select: 'parse create validate',
highlight: 'create validate',
view: 'create validate',
disable: 'flipItem',
enable: 'flipItem'
}
// The component's item object.
clock.item = {}
clock.item.interval = settings.interval || 30
clock.item.disable = ( settings.disable || [] ).slice( 0 )
clock.item.enable = -(function( collectionDisabled ) {
return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
})( clock.item.disable )
clock.
set( 'min', settings.min ).
set( 'max', settings.max ).
set( 'now' ).
// Setting the `select` also sets the `highlight` and `view`.
set( 'select',
// If there's a `value` or `data-value`, use that with formatting.
// Otherwise default to the minimum selectable time.
elementDataValue || picker.$node[ 0 ].value || clock.item.min,
// Use the relevant format.
{ format: elementDataValue ? settings.formatSubmit : settings.format }
)
// The keycode to movement mapping.
clock.key = {
40: 1, // Down
38: -1, // Up
39: 1, // Right
37: -1, // Left
go: function( timeChange ) {
clock.set( 'highlight', clock.item.highlight.pick + timeChange * clock.item.interval, { interval: timeChange * clock.item.interval } )
this.render()
}
}
// Bind some picker events.
picker.
on( 'render', function() {
var $pickerHolder = picker.$root.children(),
$viewset = $pickerHolder.find( '.' + settings.klass.viewset )
if ( $viewset.length ) {
$pickerHolder[ 0 ].scrollTop = ~~$viewset.position().top - ( $viewset[ 0 ].clientHeight * 2 )
}
}).
on( 'open', function() {
picker.$root.find( 'button' ).attr( 'disable', false )
}).
on( 'close', function() {
picker.$root.find( 'button' ).attr( 'disable', true )
})
} //TimePicker
/**
* Set a timepicker item object.
*/
TimePicker.prototype.set = function( type, value, options ) {
var clock = this
// Go through the queue of methods, and invoke the function. Update this
// as the time unit, and set the final resultant as this item type.
// * In the case of `enable`, keep the queue but set `disable` instead.
// And in the case of `flip`, keep the queue but set `enable` instead.
clock.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) {
return value = clock[ method ]( type, value, options )
}).pop()
// Check if we need to cascade through more updates.
if ( type == 'select' ) {
clock.set( 'highlight', clock.item.select, options )
}
else if ( type == 'highlight' ) {
clock.set( 'view', clock.item.highlight, options )
}
else if ( type == 'interval' ) {
clock.
set( 'min', clock.item.min, options ).
set( 'max', clock.item.max, options )
}
else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && clock.item.select && clock.item.highlight ) {
if ( type == 'min' ) {
clock.set( 'max', clock.item.max, options )
}
clock.
set( 'select', clock.item.select, options ).
set( 'highlight', clock.item.highlight, options )
}
return clock
} //TimePicker.prototype.set
/**
* Get a timepicker item object.
*/
TimePicker.prototype.get = function( type ) {
return this.item[ type ]
} //TimePicker.prototype.get
/**
* Create a picker time object.
*/
TimePicker.prototype.create = function( type, value, options ) {
var clock = this
// If theres no value, use the type as the value.
value = value === undefined ? type : value
// If its a date object, convert it into an array.
if ( Picker._.isDate( value ) ) {
value = [ value.getHours(), value.getMinutes() ]
}
// If its an object, use the “pick” value.
if ( $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
value = value.pick
}
// If its an array, convert it into minutes.
else if ( $.isArray( value ) ) {
value = +value[ 0 ] * MINUTES_IN_HOUR + (+value[ 1 ])
}
// If no valid value is passed, set it to “now”.
else if ( !Picker._.isInteger( value ) ) {
value = clock.now( type, value, options )
}
// If were setting the max, make sure its greater than the min.
if ( type == 'max' && value < clock.item.min.pick ) {
value += MINUTES_IN_DAY
}
// If the value doesnt fall directly on the interval,
// add one interval to indicate it as “passed”.
if ( type != 'min' && type != 'max' && (value - clock.item.min.pick) % clock.item.interval !== 0 ) {
value += clock.item.interval
}
// Normalize it into a “reachable” interval.
value = clock.normalize( type, value, options )
// Return the compiled object.
return {
// Divide to get hours from minutes.
hour: ~~( HOURS_IN_DAY + value / MINUTES_IN_HOUR ) % HOURS_IN_DAY,
// The remainder is the minutes.
mins: ( MINUTES_IN_HOUR + value % MINUTES_IN_HOUR ) % MINUTES_IN_HOUR,
// The time in total minutes.
time: ( MINUTES_IN_DAY + value ) % MINUTES_IN_DAY,
// Reference to the “relative” value to pick.
pick: value
}
} //TimePicker.prototype.create
/**
* Get the time relative to now.
*/
TimePicker.prototype.now = function( type, value/*, options*/ ) {
var date = new Date(),
dateMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes()
// Make sure “now” falls within the interval range.
dateMinutes -= dateMinutes % this.item.interval
// If the value is a number, adjust by that many intervals because
// the time has passed. In the case of “midnight” and a negative `min`,
// increase the value by 2. Otherwise increase it by 1.
if ( Picker._.isInteger( value ) ) {
value += type == 'min' && value < 0 && dateMinutes === 0 ? 2 : 1
}
// If the value isnt a number, default to 1 passed interval.
else {
value = 1
}
// Calculate the final relative time.
return value * this.item.interval + dateMinutes
} //TimePicker.prototype.now
/**
* Normalize minutes to be “reachable” based on the min and interval.
*/
TimePicker.prototype.normalize = function( type, value/*, options*/ ) {
var interval = this.item.interval,
// If setting min time, dont shift anything.
// Otherwise get the value and min difference and then
// normalize the difference with the interval.
difference = type == 'min' ? 0 : ( value - this.item.min.pick ) % interval
// If its a negative value, add one interval to keep it as “passed”.
return value - ( difference + ( value < 0 ? interval : 0 ) )
} //TimePicker.prototype.normalize
/**
* Measure the range of minutes.
*/
TimePicker.prototype.measure = function( type, value, options ) {
var clock = this
// If its anything false-y, set it to the default.
if ( !value ) {
value = type == 'min' ? [ 0, 0 ] : [ HOURS_IN_DAY - 1, MINUTES_IN_HOUR - 1 ]
}
// If its a literal true, or an integer, make it relative to now.
else if ( value === true || Picker._.isInteger( value ) ) {
value = clock.now( type, value, options )
}
// If its an object already, just normalize it.
else if ( $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
value = clock.normalize( type, value.pick, options )
}
return value
} ///TimePicker.prototype.measure
/**
* Validate an object as enabled.
*/
TimePicker.prototype.validate = function( type, timeObject, options ) {
var clock = this,
interval = options && options.interval ? options.interval : clock.item.interval
// Check if the object is disabled.
if ( clock.disabled( timeObject ) ) {
// Shift with the interval until we reach an enabled time.
timeObject = clock.shift( timeObject, interval )
}
// Scope the object into range.
timeObject = clock.scope( timeObject )
// Do a second check to see if we landed on a disabled min/max.
// In that case, shift using the opposite interval as before.
if ( clock.disabled( timeObject ) ) {
timeObject = clock.shift( timeObject, interval * -1 )
}
// Return the final object.
return timeObject
} //TimePicker.prototype.validate
/**
* Check if an object is disabled.
*/
TimePicker.prototype.disabled = function( timeObject ) {
var
clock = this,
// Filter through the disabled times to check if this is one.
isDisabledMatch = clock.item.disable.filter( function( timeToDisable ) {
// If the time is a number, match the hours.
if ( Picker._.isInteger( timeToDisable ) ) {
return timeObject.hour == timeToDisable
}
// If its an array, create the object and match the times.
if ( $.isArray( timeToDisable ) || Picker._.isDate( timeToDisable ) ) {
return timeObject.pick == clock.create( timeToDisable ).pick
}
})
// If this time matches a disabled time, confirm its not inverted.
isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( timeToDisable ) {
return $.isArray( timeToDisable ) && timeToDisable[2] == 'inverted'
}).length
// If the clock is "enabled" flag is flipped, flip the condition.
return clock.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
timeObject.pick < clock.item.min.pick ||
timeObject.pick > clock.item.max.pick
} //TimePicker.prototype.disabled
/**
* Shift an object by an interval until we reach an enabled object.
*/
TimePicker.prototype.shift = function( timeObject, interval ) {
var clock = this,
minLimit = clock.item.min.pick,
maxLimit = clock.item.max.pick
interval = interval || clock.item.interval
// Keep looping as long as the time is disabled.
while ( clock.disabled( timeObject ) ) {
// Increase/decrease the time by the interval and keep looping.
timeObject = clock.create( timeObject.pick += interval )
// If we've looped beyond the limits, break out of the loop.
if ( timeObject.pick <= minLimit || timeObject.pick >= maxLimit ) {
break
}
}
// Return the final object.
return timeObject
} //TimePicker.prototype.shift
/**
* Scope an object to be within range of min and max.
*/
TimePicker.prototype.scope = function( timeObject ) {
var minLimit = this.item.min.pick,
maxLimit = this.item.max.pick
return this.create( timeObject.pick > maxLimit ? maxLimit : timeObject.pick < minLimit ? minLimit : timeObject )
} //TimePicker.prototype.scope
/**
* Parse a string into a usable type.
*/
TimePicker.prototype.parse = function( type, value, options ) {
var clock = this,
parsingObject = {}
if ( !value || Picker._.isInteger( value ) || $.isArray( value ) || Picker._.isDate( value ) || $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
return value
}
// We need a `.format` to parse the value.
if ( !( options && options.format ) ) {
throw "Need a formatting option to parse this.."
}
// Convert the format into an array and then map through it.
clock.formats.toArray( options.format ).map( function( label ) {
var
// Grab the formatting label.
formattingLabel = clock.formats[ label ],
// The format length is from the formatting label function or the
// label length without the escaping exclamation (!) mark.
formatLength = formattingLabel ? Picker._.trigger( formattingLabel, clock, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
// If there's a format label, split the value up to the format length.
// Then add it to the parsing object with appropriate label.
if ( formattingLabel ) {
parsingObject[ label ] = value.substr( 0, formatLength )
}
// Update the time value as the substring from format length to end.
value = value.substr( formatLength )
})
return +parsingObject.i + MINUTES_IN_HOUR * (
+( parsingObject.H || parsingObject.HH ) ||
( +( parsingObject.h || parsingObject.hh ) % 12 + ( /^p/i.test( parsingObject.A || parsingObject.a ) ? 12 : 0 ) )
)
} //TimePicker.prototype.parse
/**
* Various formats to display the object in.
*/
TimePicker.prototype.formats = {
h: function( string, timeObject ) {
// If there's string, then get the digits length.
// Otherwise return the selected hour in "standard" format.
return string ? Picker._.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON
},
hh: function( string, timeObject ) {
// If there's a string, then the length is always 2.
// Otherwise return the selected hour in "standard" format with a leading zero.
return string ? 2 : Picker._.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON )
},
H: function( string, timeObject ) {
// If there's string, then get the digits length.
// Otherwise return the selected hour in "military" format as a string.
return string ? Picker._.digits( string ) : '' + ( timeObject.hour % 24 )
},
HH: function( string, timeObject ) {
// If there's string, then get the digits length.
// Otherwise return the selected hour in "military" format with a leading zero.
return string ? Picker._.digits( string ) : Picker._.lead( timeObject.hour % 24 )
},
i: function( string, timeObject ) {
// If there's a string, then the length is always 2.
// Otherwise return the selected minutes.
return string ? 2 : Picker._.lead( timeObject.mins )
},
a: function( string, timeObject ) {
// If there's a string, then the length is always 4.
// Otherwise check if it's more than "noon" and return either am/pm.
return string ? 4 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'a.m.' : 'p.m.'
},
A: function( string, timeObject ) {
// If there's a string, then the length is always 2.
// Otherwise check if it's more than "noon" and return either am/pm.
return string ? 2 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'AM' : 'PM'
},
// Create an array by splitting the formatting string passed.
toArray: function( formatString ) { return formatString.split( /(h{1,2}|H{1,2}|i|a|A|!.)/g ) },
// Format an object into a string using the formatting options.
toString: function ( formatString, itemObject ) {
var clock = this
return clock.formats.toArray( formatString ).map( function( label ) {
return Picker._.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' )
}).join( '' )
}
} //TimePicker.prototype.formats
/**
* Flip an item as enabled or disabled.
*/
TimePicker.prototype.flipItem = function( type, value/*, options*/ ) {
var clock = this,
collection = clock.item.disable,
isFlippedBase = clock.item.enable === -1
// Flip the enabled and disabled times.
if ( value == 'flip' ) {
clock.item.enable = isFlippedBase ? 1 : -1
}
// Reset the collection and enable the base state.
else if ( ( type == 'enable' && value === true ) || ( type == 'disable' && value === false ) ) {
clock.item.enable = 1
collection = []
}
// Reset the collection and disable the base state.
else if ( ( type == 'enable' && value === false ) || ( type == 'disable' && value === true ) ) {
clock.item.enable = -1
collection = []
}
// Make sure a collection of things was passed to add/remove.
else if ( $.isArray( value ) ) {
// Check if we have to add/remove from collection.
if ( isFlippedBase && type == 'enable' || !isFlippedBase && type == 'disable' ) {
collection = clock.addDisabled( collection, value )
}
else if ( !isFlippedBase && type == 'enable' ) {
collection = clock.addEnabled( collection, value )
}
else if ( isFlippedBase && type == 'disable' ) {
collection = clock.removeDisabled( collection, value )
}
}
return collection
} //TimePicker.prototype.flipItem
/**
* Add an enabled (inverted) item to the disabled collection.
*/
TimePicker.prototype.addEnabled = function( collection, item ) {
var clock = this
// Go through each item to enable.
item.map( function( timeUnit ) {
// Check if the time unit is already within the collection.
if ( clock.filterDisabled( collection, timeUnit, 1 ).length ) {
// Remove the unit directly from the collection.
collection = clock.removeDisabled( collection, [timeUnit] )
// If the unit is an array and it falls within a
// disabled weekday, invert it and then insert it.
if (
$.isArray( timeUnit ) &&
collection.filter( function( disabledHour ) {
return Picker._.isInteger( disabledHour ) && clock.create( timeUnit ).hour === disabledHour
}).length
) {
timeUnit = timeUnit.slice(0)
timeUnit.push( 'inverted' )
collection.push( timeUnit )
}
}
})
// Return the final collection.
return collection
} //TimePicker.prototype.addEnabled
/**
* Add an item to the disabled collection.
*/
TimePicker.prototype.addDisabled = function( collection, item ) {
var clock = this
// Go through each item to disable.
item.map( function( timeUnit ) {
// Add the time unit if it isnt already within the collection.
if ( !clock.filterDisabled( collection, timeUnit ).length ) {
collection.push( timeUnit )
}
// If the time unit is an array and falls within the range, just remove it.
else if ( $.isArray( timeUnit ) && clock.filterDisabled( collection, timeUnit, 1 ).length ) {
collection = clock.removeDisabled( collection, [timeUnit] )
}
})
// Return the final collection.
return collection
} //TimePicker.prototype.addDisabled
/**
* Remove an item from the disabled collection.
*/
TimePicker.prototype.removeDisabled = function( collection, item ) {
var clock = this
// Go through each item to enable.
item.map( function( timeUnit ) {
// Filter each item out of the collection.
collection = clock.filterDisabled( collection, timeUnit, 1 )
})
// Return the final colleciton.
return collection
} //TimePicker.prototype.removeDisabled
/**
* Filter through the disabled collection to find a time unit.
*/
TimePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) {
var timeIsArray = $.isArray( timeUnit )
return collection.filter( function( disabledTimeUnit ) {
var isMatch = !timeIsArray && timeUnit === disabledTimeUnit ||
timeIsArray && $.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString()
return isRemoving ? !isMatch : isMatch
})
} //TimePicker.prototype.filterDisabled
/**
* The division to use for the range intervals.
*/
TimePicker.prototype.i = function( type, value/*, options*/ ) {
return Picker._.isInteger( value ) && value > 0 ? value : this.item.interval
}
/**
* Create a string for the nodes in the picker.
*/
TimePicker.prototype.nodes = function( isOpen ) {
var
clock = this,
settings = clock.settings,
selectedObject = clock.item.select,
highlightedObject = clock.item.highlight,
viewsetObject = clock.item.view,
disabledCollection = clock.item.disable
return Picker._.node( 'ul', Picker._.group({
min: clock.item.min.pick,
max: clock.item.max.pick,
i: clock.item.interval,
node: 'li',
item: function( loopedTime ) {
loopedTime = clock.create( loopedTime )
return [
Picker._.trigger( clock.formats.toString, clock, [ Picker._.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ),
(function( klasses, timeMinutes ) {
if ( selectedObject && selectedObject.pick == timeMinutes ) {
klasses.push( settings.klass.selected )
}
if ( highlightedObject && highlightedObject.pick == timeMinutes ) {
klasses.push( settings.klass.highlighted )
}
if ( viewsetObject && viewsetObject.pick == timeMinutes ) {
klasses.push( settings.klass.viewset )
}
if ( disabledCollection && clock.disabled( loopedTime ) ) {
klasses.push( settings.klass.disabled )
}
return klasses.join( ' ' )
})( [ settings.klass.listItem ], loopedTime.pick ),
'data-pick=' + loopedTime.pick
]
}
}) +
// * For Firefox forms to submit, make sure to set the buttons `type` attribute as “button”.
Picker._.node( 'li', Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'type=button data-clear=1' + ( isOpen ? '' : ' disable' ) ) ), settings.klass.list )
} //TimePicker.prototype.nodes
/* ==========================================================================
Extend the picker to add the component with the defaults.
========================================================================== */
TimePicker.defaults = (function( prefix ) {
return {
// Clear
clear: 'Clear',
// The format to show on the `input` element
format: 'h:i A',
// The interval between each time
interval: 30,
// Classes
klass: {
picker: prefix + ' ' + prefix + '--time',
holder: prefix + '__holder',
list: prefix + '__list',
listItem: prefix + '__list-item',
disabled: prefix + '__list-item--disabled',
selected: prefix + '__list-item--selected',
highlighted: prefix + '__list-item--highlighted',
viewset: prefix + '__list-item--viewset',
now: prefix + '__list-item--now',
buttonClear: prefix + '__button--clear'
}
}
})( Picker.klasses().picker )
/**
* Extend the picker to add the date picker.
*/
Picker.extend( 'pickatime', TimePicker )
}));