/**
* JTSage-DateBox
* @fileOverview Handle date limits
* @author J.T.Sage <jtsage+datebox@gmail.com>
* @author {@link https://github.com/jtsage/jtsage-datebox/contributors|GitHub Contributors}
* @license {@link https://github.com/jtsage/jtsage-datebox/blob/master/LICENSE.txt|MIT}
* @version 5.2.0
*/
/**
* Fix minutes of the date based on minuteStep option.
*
* Round according to minStepRound
*/
JTSageDateBox._minStepFix = function() {
// Round "extra" minutes when using a stepper.
var newMinute = this.theDate.get(4),
mstep = this.options.minuteStep,
roundDirection = this.options.minStepRound,
remainder = newMinute % mstep;
if ( mstep > 1 && remainder > 0 ) {
if ( roundDirection < 0 ) {
newMinute = newMinute - remainder;
} else if ( roundDirection > 0 ) {
newMinute = newMinute + ( mstep - remainder );
} else {
if ( newMinute % mstep < mstep / 2 ) {
newMinute = newMinute - remainder;
} else {
newMinute = newMinute + ( mstep - remainder );
}
}
this.theDate.setMinutes(newMinute);
}
};
/**
* Contains functions to check the date
*
* All functions expect a date object, and return a boolean for qualification
*
* @type {Object}
* @property {function} enableDate Date exists in enableDates option
* @property {function} whiteDate Date exists in whiteDates option
* @property {function} notToday Date is today
* @property {function} maxYear Date is beyond maxYear
* @property {function} minYear Date is prior to minYear
* @property {function} afterToday Date is after today
* @property {function} beforeToday Date is before today
* @property {function} minDays Date is before minimum
* @property {function} maxDays Date is after maximum
* @property {function} minHour Time is before minimum
* @property {function} maxHour Time is after maximum
* @property {function} minTime Time is before minimum
* @property {function} maxTime Time is after maximum
* @property {function} validHours Hour is in validHours option
* @property {function} blackDays Day is in blackDays option
* @property {function} blackDates Date is in blackDates
* @property {function} blackDatesRec Date is in blackDatesRec
* @property {function} blackDatesPeriod Date is in blackDatesPeriod
*/
JTSageDateBox._newDateCheck = {
/* NOTE: These return true if the test passes. i.e., dobule negatives galore. */
enableDate : function ( testDate ) {
/* Return true if the date is whitelisted */
return ( this.options.enableDates.indexOf( testDate.iso ) > -1 );
},
whiteDate : function ( testDate ) {
/* Return true if the date is whitelisted */
if ( this.options.whiteDates === false ) { return false; }
return ( this.options.whiteDates.indexOf( testDate.iso ) > -1 );
},
notToday : function ( testDate ) {
/* Return true if the date *is* today */
if ( this.options.notToday === false ) { return false; }
return ( this.realToday.comp() === testDate.comp() );
},
maxYear : function ( testDate ) {
/* return true if the date is beyond the max year */
var testOption = this.options.maxYear;
if ( testOption === false ) { return false; }
return ( testDate.get(0) > testOption );
},
minYear : function ( testDate ) {
/* return true if the date is beyond the max year */
var testOption = this.options.minYear;
if ( testOption === false ) { return false; }
return ( testDate.get(0) < testOption );
},
minDate : function ( testDate ) {
/* return true if the date is before the minimum */
var testOption = this.options.minDate;
if ( testOption === false ) { return false; }
testOption = this.parseISO( testOption );
return ( testDate < testOption );
},
maxDate : function ( testDate ) {
/* return true if the date is after the minimum */
var testOption = this.options.maxDate;
if ( testOption === false ) { return false; }
testOption = this.parseISO( testOption );
testOption.adj(2,1);
return ( testOption < testDate );
},
afterToday : function ( testDate ) {
/* Return true if the date is BEFORE today's date (dates AFTER are allowed) */
var testOption = this.options.afterToday;
if ( testOption === false ) { return false; }
return ( testDate < this.realToday );
},
beforeToday : function ( testDate ) {
/* return true if the date is AFTER today's date (dates BEFORE are allowed) */
var testOption = this.options.beforeToday;
if ( testOption === false ) { return false; }
return ( testDate > this.realToday );
},
minmaxDays : function ( testDate ) {
/* return true if the date is invalid (too many days before today) */
var testOption1 = this.options.minDays,
testOption2 = this.options.maxDays,
validMin, validMax;
if ( testOption1 === false && testOption2 === false ) {
return false;
}
validMin = ( testOption1 === false ) ?
true :
( this.realToday.getEpochDays() - ( testOption1 + 1 ) < testDate.getEpochDays() );
validMax = ( testOption2 === false ) ?
true :
( this.realToday.getEpochDays() + ( testOption2 + 1 ) > testDate.getEpochDays() );
return ! ( validMin && validMax );
},
minHour : function ( testDate ) {
/* return true if the time is invalid (hour before allowed) */
var testOption = this.options.minHour;
if ( testOption === false ) { return false; }
return ( testDate.get(3) < testOption );
},
maxHour : function ( testDate ) {
/* return true if the time is invalid (hour after allowed) */
var testOption = this.options.maxHour;
if ( testOption === false ) { return false; }
return ( testDate.get(3) > testOption );
},
minTime : function ( testDate ) {
/* return true if the time is before the minimum allowed */
var testOption = this.options.minTime,
splitOption = null,
testHour = testDate.get(3);
if ( testOption === false ) { return false; }
splitOption = this.options.minTime.split(":", 2);
// Hour is before allowed, fail
if ( testHour < splitOption[0] ) { return true; }
// Hour is after allowed, pass
if ( testHour > splitOption[0] ) { return false; }
// Hour is the same, check minutes
return ( testDate.get(4) < splitOption[1] );
},
maxTime : function ( testDate ) {
/* return true if the time is after the maximum allowed */
var testOption = this.options.maxTime,
splitOption = null,
testHour = testDate.get(3);
if ( testOption === false ) { return false; }
splitOption = this.options.maxTime.split(":", 2);
// Hour is before allowed, pass
if ( testHour < splitOption[0] ) { return false; }
// Hour is after allowed, fail
if ( testHour > splitOption[0] ) { return true; }
// Hour is the same, check minutes
return ( testDate.get(4) > splitOption[1] );
},
validHours : function ( testDate ) {
/* return true if the hour *IS VALID* */
return ( this.options.validHours.indexOf( testDate.get(3) ) > -1 );
},
blackDays : function ( testDate ) {
/* return true if the date matched blacked out days of the week */
var testOption = this.options.blackDays;
if ( testOption === false ) { return false; }
return ( testOption.indexOf( testDate.getDay() ) > -1 );
},
blackDates : function ( testDate ) {
/* return true if the date is blacklisted */
var testOption = this.options.blackDates;
if ( testOption === false ) { return false; }
return ( testOption.indexOf( testDate.iso() ) > -1 );
},
blackDatesRec : function ( testDate ) {
/* return true if the date is blacklisted in the recurring dates */
var i, testOption = this.options.blackDatesRec;
if ( testOption === false ) { return false; }
for ( i = 0; i < testOption.length; i++ ) {
if (
( testOption[i][0] === -1 || testOption[i][0] === testDate.get(0) ) &&
( testOption[i][1] === -1 || testOption[i][1] === testDate.get(1) ) &&
( testOption[i][2] === -1 || testOption[i][2] === testDate.get(2) )
) { return true ;}
}
return false;
},
blackDatesPeriod : function ( testDate ) {
/* return true if the date is blacklisted in the period */
var i, j, k, testOption = this.options.blackDatesPeriod;
if ( testOption === false ) { return false; }
i = testOption[0].split("-");
j = new Date(i[0], i[1]-1, i[2], 12, 1, 1, 1);
k = Math.round(
( testDate.getTime() - j.getTime() ) / ( 1000 * 3600 * 24 )
);
if ( ( k % testOption[1] ) === 0 ) {
return true;
} else {
return false;
}
}
};
/**
* @typedef {object} _newDateChecker_Return
* @property {boolean} good Date is good
* @property {boolean} bad Date is bad
* @property {string|boolean} failrule Rule the date failed, or false
* @property {string|boolean} passrule Rule the date passed, or false
* @property {object} dateObj Date object
*/
/**
* Check if the date is valid.
*
* Note both failrule and passrule can be false if the date is valid but not
* explicitly valid.
*
* @param {object}
* @return {_newDateChecker_Return}
*/
JTSageDateBox._newDateChecker = function( testDate ) {
var w = this,
itt, done = false,
returnObject = {
good : true,
bad : false,
failrule : false,
passrule : false,
dateObj : testDate.copy()
},
badChecks = [
"blackDays", "blackDates", "blackDatesRec", "blackDatesPeriod",
"notToday", "maxYear", "minYear", "afterToday", "beforeToday",
"maxDate", "minDate", "minmaxDays",
"minHour", "maxHour", "minTime", "maxTime"
];
w.realToday = new w._date();
// If "enableDates" is in use, no other checks are performed
if ( this.options.enableDates !== false ) {
if ( w._newDateCheck.whiteDate.call( w, testDate ) ) {
returnObject.passrule = "enableDates";
} else {
returnObject.bad = true;
returnObject.good = false;
returnObject.failrule = "enableDates";
}
return returnObject;
}
// If "validHours" is in use, no other checks are performed
if ( this.options.validHours !== false ) {
if ( w._newDateCheck.validHours.call( w, testDate ) ) {
returnObject.passrule = "validHours";
} else {
returnObject.bad = true;
returnObject.good = false;
returnObject.failrule = "validHours";
}
return returnObject;
}
// If a date is "whiteDates", no other checks are performed
if ( w._newDateCheck.whiteDate.call( w, testDate ) ) {
returnObject.passrule = "whiteDates";
return returnObject;
}
for ( itt = 0; itt < badChecks.length && !done; itt++ ) {
if ( w._newDateCheck[ badChecks[ itt ] ].call( w, testDate ) ) {
returnObject.bad = true;
returnObject.good = false;
returnObject.failrule = badChecks[ itt ];
done = true;
}
}
return returnObject;
};
/**
* @member {number} lastDuration Last entered duration in seconds
* @memberOf JTSageDateBox
*/
/**
* @member {array} lastDurationA Last entered duration, [ days, hours, minutes, seconds ]
* @memberOf JTSageDateBox
*/
/**
* Clean up the duration amount.
*
* Checks for negatives, and applies minDur/maxDur if set
*
* Returns nothing but sets {@link JTSageDateBox.lastDuration} and
* {@link JTSageDateBox.lastDurationA}
*/
JTSageDateBox._getCleanDur = function() {
var w = this,
o = this.options,
thisDuration = w.theDate.getEpoch() - w.initDate.getEpoch();
// Check for less than zero. (and fix it)
if ( thisDuration < 0 ) {
thisDuration = 0;
w.theDate = w.initDate.copy();
}
if ( o.minDur !== false && thisDuration < o.minDur ) {
w.theDate = new w._date( w.initDate.getTime() + ( o.minDur * 1000 ) );
thisDuration = o.minDur;
}
if ( o.maxDur !== false && thisDuration > o.maxDur ) {
w.theDate = new w._date( w.initDate.getTime() + ( o.maxDur * 1000 ) );
thisDuration = o.maxDur;
}
w.lastDuration = thisDuration;
w.lastDurationA = w._dur( thisDuration * 1000 );
return [ thisDuration, w._dur( thisDuration * 1000 ) ];
};
/**
* @member {boolean} dateOK The selected date is valid
* @memberOf JTSageDateBox
*/
/**
* Check if the date is good - older method.
*
* Also sets {@link JTSageDateBox.dateOK}
*
* @return {boolean} Date is good
*/
JTSageDateBox._check = function() {
// Check to see if a date is valid. (Old way, left as a shim)
var checkObj = this._newDateChecker( this.theDate );
this.dateOK = ( checkObj.good === true );
return checkObj.good;
};
/**
* This makes durationStep apply to the least precise duration
* field. Stepping an earlier field has rather unexpected results.
*
* @param {array} order Field display order
*/
JTSageDateBox._fixstepper = function( order ) {
// Turn back off steppers when displaying a less precise
// unit in the same control.
var step = this.options.durationSteppers,
actual = this.options.durationStep;
if ( order.indexOf( "d" ) > -1 ) {
step.d = actual;
}
if ( order.indexOf( "h" ) > -1 ) {
step.d = 1;
step.h = actual;
}
if ( order.indexOf( "i" ) > -1 ) {
step.h = 1;
step.i = actual;
}
if ( order.indexOf( "s" ) > -1 ) {
step.i = 1;
step.s = actual;
}
};
/**
* Contains functions to choose the appropriate theme.
*
* All functions expect a date object, and return a boolean for qualification
*
* @type {Object}
* @property {function} selected Date is currently selected
* @property {function} today Date is today
* @property {function} highDates Date is in the highDates array
* @property {function} highDatesAlt Date is in the highDatesAlt array
* @property {function} highDatesRec Date is referenced in the highDatesRec option
* @property {function} highDatesPeriod Date is referenced in the highDatesPeriod option
* @property {function} highDays Day is in the highDays array
*/
JTSageDateBox._ThemeDateCK = {
selected : function ( testDate ) {
if ( this.options.slideHighPick === false ) { return false; }
if ( typeof this.originalDate === "undefined" ) { return false; }
return ( this.originalDate.iso() === testDate.iso() );
},
today : function ( testDate ) {
if ( this.options.slideHighToday === false ) { return false; }
return ( this.realToday.iso() === testDate.iso() );
},
highDates : function ( testDate ) {
/* Return true if found */
var testOption = this.options.highDates;
if ( testOption === false ) { return false; }
return ( testOption.indexOf( testDate.iso() ) > -1 );
},
highDatesAlt : function ( testDate ) {
/* Return true if found */
var testOption = this.options.highDatesAlt;
if ( testOption === false ) { return false; }
return ( testOption.indexOf( testDate.iso() ) > -1 );
},
highDatesRec : function ( testDate ) {
/* return true if the date is listed in the recurring dates */
var i, testOption = this.options.highDatesRec;
if ( testOption === false ) { return false; }
for ( i = 0; i < testOption.length; i++ ) {
if (
( testOption[i][0] === -1 || testOption[i][0] === testDate.get( 0 ) ) &&
( testOption[i][1] === -1 || testOption[i][1] === testDate.get( 1 ) ) &&
( testOption[i][2] === -1 || testOption[i][2] === testDate.get( 2 ) )
) { return true ;}
}
return false;
},
highDatesPeriod : function ( testDate ) {
/* return true if the date is in the period */
var i, j, k, testOption = this.options.highDatesPeriod;
if ( testOption === false ) { return false; }
i = testOption[0].split("-");
j = new Date(i[0], i[1]-1, i[2], 12, 1, 1, 1);
k = Math.round(
( testDate.getTime() - j.getTime() ) / ( 1000 * 3600 * 24 )
);
if ( ( k % testOption[1] ) === 0 ) {
return true;
} else {
return false;
}
},
highDays : function ( testDate ) {
/* return true if the date matched blacked out days of the week */
var testOption = this.options.highDays;
if ( testOption === false ) { return false; }
return ( testOption.indexOf( testDate.getDay() ) > -1 );
}
};