Files
2026-02-07 08:23:18 +01:00

1061 lines
38 KiB
JavaScript
Raw Permalink 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.
/*!
* Date picker for pickadate.js v3.3.2
* http://amsul.github.io/pickadate.js/date.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 DAYS_IN_WEEK = 7,
WEEKS_IN_CALENDAR = 6
/**
* The date picker constructor
*/
function DatePicker( picker, settings ) {
var calendar = this,
elementValue = picker.$node[ 0 ].value,
elementDataValue = picker.$node.data( 'value' ),
valueString = elementDataValue || elementValue,
formatString = elementDataValue ? settings.formatSubmit : settings.format,
isRTL = function() {
return getComputedStyle( picker.$root[0] ).direction === 'rtl'
}
calendar.settings = settings
// The queue of methods that will be used to build item objects.
calendar.queue = {
min: 'measure create',
max: 'measure create',
now: 'now create',
select: 'parse create validate',
highlight: 'navigate create validate',
view: 'create validate viewset',
disable: 'flipItem',
enable: 'flipItem'
}
// The component's item object.
calendar.item = {}
calendar.item.disable = ( settings.disable || [] ).slice( 0 )
calendar.item.enable = -(function( collectionDisabled ) {
return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
})( calendar.item.disable )
calendar.
set( 'min', settings.min ).
set( 'max', settings.max ).
set( 'now' ).
// Setting the `select` also sets the `highlight` and `view`.
set( 'select',
// Use the value provided or default to selecting “today”.
valueString || calendar.item.now,
{
// Use the appropriate format.
format: formatString,
// Set user-provided month data as true when there is a
// “mm” or “m” used in the relative format string.
data: (function( formatArray ) {
return valueString && ( formatArray.indexOf( 'mm' ) > -1 || formatArray.indexOf( 'm' ) > -1 )
})( calendar.formats.toArray( formatString ) )
}
)
// The keycode to movement mapping.
calendar.key = {
40: 7, // Down
38: -7, // Up
39: function() { return isRTL() ? -1 : 1 }, // Right
37: function() { return isRTL() ? 1 : -1 }, // Left
go: function( timeChange ) {
calendar.set( 'highlight', [ calendar.item.highlight.year, calendar.item.highlight.month, calendar.item.highlight.date + timeChange ], { interval: timeChange } )
this.render()
}
}
// Bind some picker events.
picker.
on( 'render', function() {
picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
picker.set( 'highlight', [ picker.get( 'view' ).year, this.value, picker.get( 'highlight' ).date ] )
picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' )
})
picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
picker.set( 'highlight', [ this.value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' )
})
}).
on( 'open', function() {
picker.$root.find( 'button, select' ).attr( 'disabled', false )
}).
on( 'close', function() {
picker.$root.find( 'button, select' ).attr( 'disabled', true )
})
} //DatePicker
/**
* Set a datepicker item object.
*/
DatePicker.prototype.set = function( type, value, options ) {
var calendar = 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.
calendar.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
return value = calendar[ method ]( type, value, options )
}).pop()
// Check if we need to cascade through more updates.
if ( type == 'select' ) {
calendar.set( 'highlight', calendar.item.select, options )
}
else if ( type == 'highlight' ) {
calendar.set( 'view', calendar.item.highlight, options )
}
else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && calendar.item.select && calendar.item.highlight ) {
calendar.
set( 'select', calendar.item.select, options ).
set( 'highlight', calendar.item.highlight, options )
}
return calendar
} //DatePicker.prototype.set
/**
* Get a datepicker item object.
*/
DatePicker.prototype.get = function( type ) {
return this.item[ type ]
} //DatePicker.prototype.get
/**
* Create a picker date object.
*/
DatePicker.prototype.create = function( type, value, options ) {
var isInfiniteValue,
calendar = this
// If theres no value, use the type as the value.
value = value === undefined ? type : value
// If its infinity, update the value.
if ( value == -Infinity || value == Infinity ) {
isInfiniteValue = value
}
// If its an object, use the native date object.
else if ( $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
value = value.obj
}
// If its an array, convert it into a date and make sure
// that its a valid date otherwise default to today.
else if ( $.isArray( value ) ) {
value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
value = Picker._.isDate( value ) ? value : calendar.create().obj
}
// If its a number or date object, make a normalized date.
else if ( Picker._.isInteger( value ) || Picker._.isDate( value ) ) {
value = calendar.normalize( new Date( value ), options )
}
// If its a literal true or any other case, set it to now.
else /*if ( value === true )*/ {
value = calendar.now( type, value, options )
}
// Return the compiled object.
return {
year: isInfiniteValue || value.getFullYear(),
month: isInfiniteValue || value.getMonth(),
date: isInfiniteValue || value.getDate(),
day: isInfiniteValue || value.getDay(),
obj: isInfiniteValue || value,
pick: isInfiniteValue || value.getTime()
}
} //DatePicker.prototype.create
/**
* Get the date today.
*/
DatePicker.prototype.now = function( type, value, options ) {
value = new Date()
if ( options && options.rel ) {
value.setDate( value.getDate() + options.rel )
}
return this.normalize( value, options )
} //DatePicker.prototype.now
/**
* Navigate to next/prev month.
*/
DatePicker.prototype.navigate = function( type, value, options ) {
if ( $.isPlainObject( value ) ) {
var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ),
year = targetDateObject.getFullYear(),
month = targetDateObject.getMonth(),
date = value.date
// Make sure the date is valid and if the month were going to doesnt have enough
// days, keep decreasing the date until we reach the months last date.
while ( Picker._.isDate( targetDateObject ) && new Date( year, month, date ).getMonth() !== month ) {
date -= 1
}
value = [ year, month, date ]
}
return value
} //DatePicker.prototype.navigate
/**
* Normalize a date by setting the hours to midnight.
*/
DatePicker.prototype.normalize = function( value/*, options*/ ) {
value.setHours( 0, 0, 0, 0 )
return value
}
/**
* Measure the range of dates.
*/
DatePicker.prototype.measure = function( type, value/*, options*/ ) {
var calendar = this
// If it's anything false-y, remove the limits.
if ( !value ) {
value = type == 'min' ? -Infinity : Infinity
}
// If it's an integer, get a date relative to today.
else if ( Picker._.isInteger( value ) ) {
value = calendar.now( type, value, { rel: value } )
}
return value
} ///DatePicker.prototype.measure
/**
* Create a viewset object based on navigation.
*/
DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
return this.create([ dateObject.year, dateObject.month, 1 ])
}
/**
* Validate a date as enabled and shift if needed.
*/
DatePicker.prototype.validate = function( type, dateObject, options ) {
var calendar = this,
// Keep a reference to the original date.
originalDateObject = dateObject,
// Make sure we have an interval.
interval = options && options.interval ? options.interval : 1,
// Check if the calendar enabled dates are inverted.
isFlippedBase = calendar.item.enable === -1,
// Check if we have any enabled dates after/before now.
hasEnabledBeforeTarget, hasEnabledAfterTarget,
// The min & max limits.
minLimitObject = calendar.item.min,
maxLimitObject = calendar.item.max,
// Check if weve reached the limit during shifting.
reachedMin, reachedMax,
// Check if the calendar is inverted and at least one weekday is enabled.
hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) {
// If theres a date, check where it is relative to the target.
if ( $.isArray( value ) ) {
var dateTime = calendar.create( value ).pick
if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
}
// Return only integers for enabled weekdays.
return Picker._.isInteger( value )
}).length
// Cases to validate for:
// [1] Not inverted and date disabled.
// [2] Inverted and some dates enabled.
// [3] Out of range.
//
// Cases to **not** validate for:
// • Navigating months.
// • Not inverted and date enabled.
// • Inverted and all dates disabled.
// • ..and anything else.
if ( !options.nav ) if (
/* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) ||
/* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) ||
/* 3 */ ( dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick )
) {
// When inverted, flip the direction if there arent any enabled weekdays
// and there are no enabled dates in the direction of the interval.
if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
interval *= -1
}
// Keep looping until we reach an enabled date.
while ( calendar.disabled( dateObject ) ) {
// If weve looped into the next/prev month, return to the original date and flatten the interval.
if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
dateObject = originalDateObject
interval = Math.abs( interval ) / interval
}
// If weve reached the min/max limit, reverse the direction and flatten the interval.
if ( dateObject.pick <= minLimitObject.pick ) {
reachedMin = true
interval = 1
}
else if ( dateObject.pick >= maxLimitObject.pick ) {
reachedMax = true
interval = -1
}
// If weve reached both limits, just break out of the loop.
if ( reachedMin && reachedMax ) {
break
}
// Finally, create the shifted date using the interval and keep looping.
dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
}
} //endif
// Return the date object settled on.
return dateObject
} //DatePicker.prototype.validate
/**
* Check if a date is disabled.
*/
DatePicker.prototype.disabled = function( dateObject ) {
var calendar = this,
// Filter through the disabled dates to check if this is one.
isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) {
// If the date is a number, match the weekday with 0index and `firstDay` check.
if ( Picker._.isInteger( dateToDisable ) ) {
return dateObject.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
}
// If its an array or a native JS date, create and match the exact date.
if ( $.isArray( dateToDisable ) || Picker._.isDate( dateToDisable ) ) {
return dateObject.pick === calendar.create( dateToDisable ).pick
}
})
// If this date matches a disabled date, confirm its not inverted.
isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) {
return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted'
}).length
// Check the calendar “enabled” flag and respectively flip the
// disabled state. Then also check if its beyond the min/max limits.
return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
dateObject.pick < calendar.item.min.pick ||
dateObject.pick > calendar.item.max.pick
} //DatePicker.prototype.disabled
/**
* Parse a string into a usable type.
*/
DatePicker.prototype.parse = function( type, value, options ) {
var calendar = 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 ) ) {
// should probably default to the default format.
throw "Need a formatting option to parse this.."
}
// Convert the format into an array and then map through it.
calendar.formats.toArray( options.format ).map( function( label ) {
var
// Grab the formatting label.
formattingLabel = calendar.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, calendar, [ 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 value as the substring from format length to end.
value = value.substr( formatLength )
})
// If its parsing a user provided month value, compensate for month 0index.
return [ parsingObject.yyyy || parsingObject.yy, +( parsingObject.mm || parsingObject.m ) - ( options.data ? 1 : 0 ), parsingObject.dd || parsingObject.d ]
} //DatePicker.prototype.parse
/**
* Various formats to display the object in.
*/
DatePicker.prototype.formats = (function() {
// Return the length of the first word in a collection.
function getWordLengthFromCollection( string, collection, dateObject ) {
// Grab the first word from the string.
var word = string.match( /\w+/ )[ 0 ]
// If there's no month index, add it to the date object
if ( !dateObject.mm && !dateObject.m ) {
dateObject.m = collection.indexOf( word )
}
// Return the length of the word.
return word.length
}
// Get the length of the first word in a string.
function getFirstWordLength( string ) {
return string.match( /\w+/ )[ 0 ].length
}
return {
d: function( string, dateObject ) {
// If there's string, then get the digits length.
// Otherwise return the selected date.
return string ? Picker._.digits( string ) : dateObject.date
},
dd: function( string, dateObject ) {
// If there's a string, then the length is always 2.
// Otherwise return the selected date with a leading zero.
return string ? 2 : Picker._.lead( dateObject.date )
},
ddd: function( string, dateObject ) {
// If there's a string, then get the length of the first word.
// Otherwise return the short selected weekday.
return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
},
dddd: function( string, dateObject ) {
// If there's a string, then get the length of the first word.
// Otherwise return the full selected weekday.
return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
},
m: function( string, dateObject ) {
// If there's a string, then get the length of the digits
// Otherwise return the selected month with 0index compensation.
return string ? Picker._.digits( string ) : dateObject.month + 1
},
mm: function( string, dateObject ) {
// If there's a string, then the length is always 2.
// Otherwise return the selected month with 0index and leading zero.
return string ? 2 : Picker._.lead( dateObject.month + 1 )
},
mmm: function( string, dateObject ) {
var collection = this.settings.monthsShort
// If there's a string, get length of the relevant month from the short
// months collection. Otherwise return the selected month from that collection.
return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
},
mmmm: function( string, dateObject ) {
var collection = this.settings.monthsFull
// If there's a string, get length of the relevant month from the full
// months collection. Otherwise return the selected month from that collection.
return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
},
yy: function( string, dateObject ) {
// If there's a string, then the length is always 2.
// Otherwise return the selected year by slicing out the first 2 digits.
return string ? 2 : ( '' + dateObject.year ).slice( 2 )
},
yyyy: function( string, dateObject ) {
// If there's a string, then the length is always 4.
// Otherwise return the selected year.
return string ? 4 : dateObject.year
},
// Create an array by splitting the formatting string passed.
toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },
// Format an object into a string using the formatting options.
toString: function ( formatString, itemObject ) {
var calendar = this
return calendar.formats.toArray( formatString ).map( function( label ) {
return Picker._.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
}).join( '' )
}
}
})() //DatePicker.prototype.formats
/**
* Flip an item as enabled or disabled.
*/
DatePicker.prototype.flipItem = function( type, value/*, options*/ ) {
var calendar = this,
collection = calendar.item.disable,
isFlippedBase = calendar.item.enable === -1
// Flip the enabled and disabled dates.
if ( value == 'flip' ) {
calendar.item.enable = isFlippedBase ? 1 : -1
}
// Reset the collection and enable the base state.
else if ( ( type == 'enable' && value === true ) || ( type == 'disable' && value === false ) ) {
calendar.item.enable = 1
collection = []
}
// Reset the collection and disable the base state.
else if ( ( type == 'enable' && value === false ) || ( type == 'disable' && value === true ) ) {
calendar.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 = calendar.addDisabled( collection, value )
}
else if ( !isFlippedBase && type == 'enable' ) {
collection = calendar.addEnabled( collection, value )
}
else if ( isFlippedBase && type == 'disable' ) {
collection = calendar.removeDisabled( collection, value )
}
}
return collection
} //DatePicker.prototype.flipItem
/**
* Add an enabled (inverted) item to the disabled collection.
*/
DatePicker.prototype.addEnabled = function( collection, item ) {
var calendar = this
// Go through each item to enable.
item.map( function( timeUnit ) {
// Check if the time unit is already within the collection.
if ( calendar.filterDisabled( collection, timeUnit, 1 ).length ) {
// Remove the unit directly from the collection.
collection = calendar.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( disabledDate ) {
return Picker._.isInteger( disabledDate ) && calendar.create( timeUnit ).day === disabledDate - 1
}).length
) {
timeUnit = timeUnit.slice(0)
timeUnit.push( 'inverted' )
collection.push( timeUnit )
}
}
})
// Return the final collection.
return collection
} //DatePicker.prototype.addEnabled
/**
* Add an item to the disabled collection.
*/
DatePicker.prototype.addDisabled = function( collection, item ) {
var calendar = this
// Go through each item to disable.
item.map( function( timeUnit ) {
// Add the time unit if it isnt already within the collection.
if ( !calendar.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 ) && calendar.filterDisabled( collection, timeUnit, 1 ).length ) {
collection = calendar.removeDisabled( collection, [timeUnit] )
}
})
// Return the final collection.
return collection
} //DatePicker.prototype.addDisabled
/**
* Remove an item from the disabled collection.
*/
DatePicker.prototype.removeDisabled = function( collection, item ) {
var calendar = this
// Go through each item to enable.
item.map( function( timeUnit ) {
// Filter each item out of the collection.
collection = calendar.filterDisabled( collection, timeUnit, 1 )
})
// Return the final colleciton.
return collection
} //DatePicker.prototype.removeDisabled
/**
* Filter through the disabled collection to find a time unit.
*/
DatePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) {
var calendar = this,
// Check if the time unit passed is an array or date object.
timeIsObject = $.isArray( timeUnit ) || Picker._.isDate( timeUnit ),
// Grab the comparison value if its an object.
timeObjectValue = timeIsObject && calendar.create( timeUnit ).pick
// Go through the disabled collection and try to match this time unit.
return collection.filter( function( disabledTimeUnit ) {
// Check if its an object and the collection item is an object,
// use the comparison values. Otherwise to a direct comparison.
var isMatch = timeIsObject && ( $.isArray( disabledTimeUnit ) || Picker._.isDate( disabledTimeUnit ) ) ?
timeObjectValue === calendar.create( disabledTimeUnit ).pick : timeUnit === disabledTimeUnit
// Invert the match if were removing from the collection.
return isRemoving ? !isMatch : isMatch
})
} //DatePicker.prototype.filterDisabled
/**
* Create a string for the nodes in the picker.
*/
DatePicker.prototype.nodes = function( isOpen ) {
var
calendar = this,
settings = calendar.settings,
nowObject = calendar.item.now,
selectedObject = calendar.item.select,
highlightedObject = calendar.item.highlight,
viewsetObject = calendar.item.view,
disabledCollection = calendar.item.disable,
minLimitObject = calendar.item.min,
maxLimitObject = calendar.item.max,
// Create the calendar table head using a copy of weekday labels collection.
// * We do a copy so we don't mutate the original array.
tableHead = (function( collection ) {
// If the first day should be Monday, move Sunday to the end.
if ( settings.firstDay ) {
collection.push( collection.shift() )
}
// Create and return the table head group.
return Picker._.node(
'thead',
Picker._.group({
min: 0,
max: DAYS_IN_WEEK - 1,
i: 1,
node: 'th',
item: function( counter ) {
return [
collection[ counter ],
settings.klass.weekdays
]
}
})
) //endreturn
})( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ) ), //tableHead
// Create the nav for next/prev month.
createMonthNav = function( next ) {
// Otherwise, return the created month tag.
return Picker._.node(
'div',
' ',
settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (
// If the focused month is outside the range, disabled the button.
( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
' ' + settings.klass.navDisabled : ''
),
'data-nav=' + ( next || -1 )
) //endreturn
}, //createMonthNav
// Create the month label.
createMonthLabel = function( monthsCollection ) {
// If there are months to select, add a dropdown menu.
if ( settings.selectMonths ) {
return Picker._.node( 'select', Picker._.group({
min: 0,
max: 11,
i: 1,
node: 'option',
item: function( loopedMonth ) {
return [
// The looped month and no classes.
monthsCollection[ loopedMonth ], 0,
// Set the value and selected index.
'value=' + loopedMonth +
( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
(
(
( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
) ?
' disabled' : ''
)
]
}
}), settings.klass.selectMonth, isOpen ? '' : 'disabled' )
}
// If there's a need for a month selector
return Picker._.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
}, //createMonthLabel
// Create the year label.
createYearLabel = function() {
var focusedYear = viewsetObject.year,
// If years selector is set to a literal "true", set it to 5. Otherwise
// divide in half to get half before and half after focused year.
numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )
// If there are years to select, add a dropdown menu.
if ( numberYears ) {
var
minYear = minLimitObject.year,
maxYear = maxLimitObject.year,
lowestYear = focusedYear - numberYears,
highestYear = focusedYear + numberYears
// If the min year is greater than the lowest year, increase the highest year
// by the difference and set the lowest year to the min year.
if ( minYear > lowestYear ) {
highestYear += minYear - lowestYear
lowestYear = minYear
}
// If the max year is less than the highest year, decrease the lowest year
// by the lower of the two: available and needed years. Then set the
// highest year to the max year.
if ( maxYear < highestYear ) {
var availableYears = lowestYear - minYear,
neededYears = highestYear - maxYear
lowestYear -= availableYears > neededYears ? neededYears : availableYears
highestYear = maxYear
}
return Picker._.node( 'select', Picker._.group({
min: lowestYear,
max: highestYear,
i: 1,
node: 'option',
item: function( loopedYear ) {
return [
// The looped year and no classes.
loopedYear, 0,
// Set the value and selected index.
'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
]
}
}), settings.klass.selectYear, isOpen ? '' : 'disabled' )
}
// Otherwise just return the year focused
return Picker._.node( 'div', focusedYear, settings.klass.year )
} //createYearLabel
// Create and return the entire calendar.
return Picker._.node(
'div',
createMonthNav() + createMonthNav( 1 ) +
createMonthLabel( settings.showMonthsShort ? settings.monthsShort : settings.monthsFull ) +
createYearLabel(),
settings.klass.header
) + Picker._.node(
'table',
tableHead +
Picker._.node(
'tbody',
Picker._.group({
min: 0,
max: WEEKS_IN_CALENDAR - 1,
i: 1,
node: 'tr',
item: function( rowCounter ) {
// If Monday is the first day and the month starts on Sunday, shift the date back a week.
var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0
return [
Picker._.group({
min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
max: function() {
return this.min + DAYS_IN_WEEK - 1
},
i: 1,
node: 'td',
item: function( targetDate ) {
// Convert the time date from a relative date to a target date.
targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ])
return [
Picker._.node(
'div',
targetDate.date,
(function( klasses ) {
// Add the `infocus` or `outfocus` classes based on month in view.
klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus )
// Add the `today` class if needed.
if ( nowObject.pick == targetDate.pick ) {
klasses.push( settings.klass.now )
}
// Add the `selected` class if something's selected and the time matches.
if ( selectedObject && selectedObject.pick == targetDate.pick ) {
klasses.push( settings.klass.selected )
}
// Add the `highlighted` class if something's highlighted and the time matches.
if ( highlightedObject && highlightedObject.pick == targetDate.pick ) {
klasses.push( settings.klass.highlighted )
}
// Add the `disabled` class if something's disabled and the object matches.
if ( disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick ) {
klasses.push( settings.klass.disabled )
}
return klasses.join( ' ' )
})([ settings.klass.day ]),
'data-pick=' + targetDate.pick
)
] //endreturn
}
})
] //endreturn
}
})
),
settings.klass.table
) +
// * For Firefox forms to submit, make sure to set the buttons `type` attributes as “button”.
Picker._.node(
'div',
Picker._.node( 'button', settings.today, settings.klass.buttonToday, 'type=button data-pick=' + nowObject.pick + ( isOpen ? '' : ' disabled' ) ) +
Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'type=button data-clear=1' + ( isOpen ? '' : ' disabled' ) ),
settings.klass.footer
) //endreturn
} //DatePicker.prototype.nodes
/**
* The date picker defaults.
*/
DatePicker.defaults = (function( prefix ) {
return {
// Months and weekdays
monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
// Today and clear
today: 'Today',
clear: 'Clear',
// The format to show on the `input` element
format: 'd mmmm, yyyy',
// Classes
klass: {
table: prefix + 'table',
header: prefix + 'header',
navPrev: prefix + 'nav--prev',
navNext: prefix + 'nav--next',
navDisabled: prefix + 'nav--disabled',
month: prefix + 'month',
year: prefix + 'year',
selectMonth: prefix + 'select--month',
selectYear: prefix + 'select--year',
weekdays: prefix + 'weekday',
day: prefix + 'day',
disabled: prefix + 'day--disabled',
selected: prefix + 'day--selected',
highlighted: prefix + 'day--highlighted',
now: prefix + 'day--today',
infocus: prefix + 'day--infocus',
outfocus: prefix + 'day--outfocus',
footer: prefix + 'footer',
buttonClear: prefix + 'button--clear',
buttonToday: prefix + 'button--today'
}
}
})( Picker.klasses().picker + '__' )
/**
* Extend the picker to add the date picker.
*/
Picker.extend( 'pickadate', DatePicker )
}));