/**
* JTSage-DateBox
* @fileOverview Provides the flipbox, timeflipbox, durationflipbox, and datetimeflipbox modes
* @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
*/
mergeOpts({
flen : {
"y" : 25,
"m" : 24,
"d" : 40,
"h" : 24,
"i" : 30,
"s" : 30,
"a" : 30,
},
durationStep : 1,
durationSteppers : {
"d" : 1,
"h" : 1,
"i" : 1,
"s" : 1
},
fboxNatural : "default",
});
/**
* Get the numbers for duration box, also deal with breaks on 24 hrs, 60 min, 60 sec.
*
* @param {string} term Field we are working on: d,h,i,s
* @param {number} offset Amount +/- from current
* @param {number} position Position in the display.
* @return {string} Text to display
*/
JTSageDateBox._fbox_do_dur_math = function ( term, offset, position ) {
var current, possibleReturn,
w = this,
multiplier = { d : Number.MAX_SAFE_INTEGER, h : 24, i : 60, s : 60 }[term];
if ( position === 0 && term !== "d" ) {
switch ( term ) {
case "h" :
current = w.lastDurationA[1] +
( w.lastDurationA[0] * 24 );
break;
case "i" :
current = w.lastDurationA[2] +
( w.lastDurationA[1] * 60 ) +
( w.lastDurationA[0] * 24 * 60 );
break;
case "s" :
current = w.lastDuration;
break;
}
} else {
current = w.lastDurationA[ ["d","h","i","s"].indexOf( term ) ];
possibleReturn = current + offset;
}
if ( position === 0 ) {
// First position just counts up indfinatly, and can't go negative.
return ( possibleReturn < 0 ) ? " " : possibleReturn;
} else {
if ( possibleReturn < 0 ) {
possibleReturn += multiplier;
}
while ( possibleReturn > ( multiplier - 1 ) ) {
possibleReturn -= multiplier;
}
return possibleReturn;
}
};
/**
* Find the appropriate tern for a flipbox element
*
* @param {string} term Which element to work on: y,m,d,h,i,s,a
* @param {number} offset +/- from the current
* @return {string} Text to display
*/
JTSageDateBox._fbox_do_roll_math = function ( term, offset ) {
// Returns an array [ text, value ]
var i,
w = this,
o = this.options,
finder = [],
current = 0,
total = 0,
safeOffset = null,
testDate;
switch ( term ) {
case "y" :
// No hard math.
return w.theDate.get(0) + offset;
case "m" :
testDate = (w.theDate.copy( false, [0,0,1] )).adj( 1, offset );
return w.__("monthsOfYearShort")[ testDate.get(1) ];
case "d" :
if ( o.rolloverMode.d === false ) {
total = 32 - w.theDate.copy([0],[0,0,32,13]).getDate();
current = w.theDate.get(3);
for ( i = 0; i < total; i++ ) {
if ( i + current > total ) {
finder.push( i + current - total);
} else {
finder.push( i + current );
}
}
safeOffset = offset % total;
if ( safeOffset < 0 ) {
return finder[ finder.length + safeOffset ];
}
return finder[ safeOffset ];
}
return w.theDate.copy( [ 0, 0, offset ]).get(2);
case "h" :
testDate = w.theDate.copy( [ 0, 0, 0, offset ] );
return ( ( w.__("timeFormat") === 12 ) ? testDate.get12hr() : testDate.get(3) );
case "i" :
return w._zPad( ( w.theDate.copy( [ 0, 0, 0, 0, offset * o.minuteStep ] )).get(4) );
case "s" :
return w._zPad( ( w.theDate.copy( [ 0, 0, 0, 0, 0, offset ] )).get(5) );
case "a" :
if ( w.theDate.get(3) > 11 ) {
// It currenly is AM, odd offset would be PM
return w.__("meridiem")[( offset % 2 === 0 ) ? 1 : 0 ];
} else {
// currently PM, odd offset would be AM
return w.__("meridiem")[( offset % 2 === 0 ) ? 0 : 1 ];
}
}
};
/**
* Build the timeflipbox
*
* @memberOf JTSageDateBox._build
* @this JTSageDateBox
*/
JTSageDateBox._build.timeflipbox = function () { this._build.flipbox.call( this ); };
/**
* Build the datetimeflipbox
*
* @memberOf JTSageDateBox._build
* @this JTSageDateBox
*/
JTSageDateBox._build.datetimeflipbox = function () { this._build.flipbox.call( this ); };
/**
* Build the durationflipbox
*
* @memberOf JTSageDateBox._build
* @this JTSageDateBox
*/
JTSageDateBox._build.durationflipbox = function () { this._build.flipbox.call( this ); };
/**
* Build the flipbox
*
* @memberOf JTSageDateBox._build
* @this JTSageDateBox
*/
JTSageDateBox._build.flipbox = function () {
var thisField, cntlFieldIdx, cntlRow,
w = this,
o = this.options,
g = this.drag,
flipContent = w.style_fboxCtr( o.theme_fbox_RollHeight ).addClass( "dbRollerV "),
cntlContain = "",
cntlRoller = "",
dur = ( o.mode === "durationflipbox" ? true : false );
// Empty internal HTML if needed. Otherwise refresh position?
if ( typeof w.d.intHTML !== "boolean" ) {
w.d.intHTML.empty().remove();
} else {
w.d.input.on( "datebox", function (e,p) {
if ( p.method === "postrefresh" ) {
w.style_fboxPos();
}
});
}
// Get apprpriate header text
w.d.headerText = w._grabLabel (
( o.mode === "datebox" || o.mode === "datetimebox" ) ?
w.__( "titleDateDialogLabel" ) :
w.__( "titleTimeDialogLabel" )
);
w.d.intHTML = $( "<span>" );
w.d.intHTML.addClass( o.theme_spanStyle );
// Choose the correct field order for the mode
w.fldOrder = w._getFldOrder( o.mode );
// If not in duration mode, check the date and reset the minute stepper
// If in duration mode, fix the duration stepper
if ( !dur ) {
w._check();
w._minStepFix();
} else {
w.dateOK = true;
w._getCleanDur();
w._fixstepper( w.fldOrder );
}
o.fboxNatural = ( o.fboxNatural === "default" ) ?
( ( dur ) ? true : false ) :
o.fboxNatural;
// Create a header for flipbox and datetimeflipbox modes
if ( o.mode === "flipbox" || o.mode === "datetimeflipbox" ) {
w.style_subHead( w._formatter( w.__( "headerFormat" ), w.theDate ) )
.appendTo( w.d.intHTML );
}
// For duration mode, create labels
if ( dur ) {
cntlContain = w.style_fboxDurLbls();
for ( cntlFieldIdx = 0; cntlFieldIdx < w.fldOrder.length; cntlFieldIdx++ ) {
thisField = w.fldOrder[ cntlFieldIdx ];
cntlContain.append( w.style_fboxDurLbl(
w.__( "durationLabel" )[ ["d","h","i","s"].indexOf( thisField ) ],
w.fldOrder.length
) );
}
if ( ( w.__( "isRTL" ) === true ) ) {
cntlContain.css({ direction : "rtl" });
}
w.d.intHTML.append( cntlContain );
}
// Build the control
for ( cntlFieldIdx = 0; cntlFieldIdx < w.fldOrder.length; cntlFieldIdx++ ) {
thisField = w.fldOrder[ cntlFieldIdx ];
cntlContain = w.style_fboxRollCtr( w.fldOrder.length ).addClass( "dbRollerC" );
cntlRoller = w.style_fboxRollPrt().addClass( "dbRoller" );
cntlContain.data({
field : thisField,
amount : ( dur ) ?
o.durationSteppers[ thisField ] :
( ( thisField === "i" ) ? o.minuteStep : 1 )
});
for ( cntlRow = -1 * o.flen[thisField]; cntlRow < ( o.flen[thisField] + 1 ); cntlRow++ ) {
if ( !dur ) {
cntlRoller.append( w.style_fboxRollCld(
w._fbox_do_roll_math( thisField, cntlRow ),
( cntlRow === 0 ) ?
( ( w.dateOK ) ? o.theme_fbox_Selected : o.theme_fbox_Forbidden ) :
o.theme_fbox_Default
) );
} else {
cntlRoller.append( w.style_fboxRollCld(
w._fbox_do_dur_math( thisField, cntlRow, cntlFieldIdx ),
( cntlRow === 0 ) ?
o.theme_fbox_Selected :
o.theme_fbox_Default
) );
}
}
if ( o.fboxNatural ) {
cntlRoller.children().each( function( i, item ) {
cntlRoller.prepend( item );
} );
}
cntlContain.append( cntlRoller );
if ( w.__( "isRTL" ) === true ) {
flipContent.prepend( cntlContain );
} else {
flipContent.append( cntlContain );
}
}
w.d.intHTML.append( flipContent );
// Add the lens
w.style_fboxLens()
.addClass( "dbLens" )
.css( { "pointerEvents" : "none", "position" : "relative" } )
.appendTo( w.d.intHTML );
// Do bottom buttons
w.d.intHTML.append( w._doBottomButtons.call( w, true ) );
// Attach events
w.d.intHTML
.on(g.eStart, ".dbRoller", function(e,f) {
if ( !g.move ) {
if ( typeof f !== "undefined" ) { e = f; }
g.move = true;
g.target = $(this).children().first();
g.pos = parseInt( g.target.css( "marginTop" ).replace( /px/i, "" ),10 );
g.start = ( e.type.substr(0,5) === "touch" ) ?
e.originalEvent.changedTouches[0].pageY :
e.pageY;
g.end = false;
g.direc = o.fboxNatural ? -1 : 1; //( dur ) ? 1 : -1;
g.velocity = 0;
g.time = Date.now();
e.stopPropagation();
e.preventDefault();
}
});
};
/**
* Build timeflipbox drag events
*
* @memberOf JTSageDateBox._drag
* @this JTSageDateBox
*/
JTSageDateBox._drag.timeflipbox = function () { this._drag.flipbox.call( this ); };
/**
* Build datetimeflipbox drag events
*
* @memberOf JTSageDateBox._drag
* @this JTSageDateBox
*/
JTSageDateBox._drag.datetimeflipbox = function () { this._drag.flipbox.call( this ); };
/**
* Build durationflipbox drag events
*
* @memberOf JTSageDateBox._drag
* @this JTSageDateBox
*/
JTSageDateBox._drag.durationflipbox = function () { this._drag.flipbox.call( this ); };
/**
* Build flipbox drag events
*
* @memberOf JTSageDateBox._drag
* @this JTSageDateBox
* @todo Rebuild this method. It works, but I've no idea how anymore
*/
JTSageDateBox._drag.flipbox = function () {
var w = this,
o = this.options,
g = this.drag;
$( document ).on( g.eMove, function(e) {
if ( g.move && o.mode.slice(-7) === "flipbox" ) {
g.end = ( e.type.substr(0,5) === "touch" ) ?
e.originalEvent.changedTouches[0].pageY :
e.pageY;
g.target.css("margin-top", (g.pos + g.end - g.start) );
g.elapsed = Date.now()-g.time;
g.velocity = 0.8 * ( 100 * (g.end - g.start) / ( 1 + g.elapsed ) ) + 0.2 * g.velocity;
e.preventDefault();
e.stopPropagation();
return false;
}
});
$( document ).on( g.eEnd, function(e) {
var eachItem, delta, currentPosition, goodPosition, totalMove, numberFull, goodGuess;
if ( g.move && o.mode.slice(-7) === "flipbox" ) {
if ( ( g.velocity < 15 && g.velocity > -15 ) || !o.useKinetic ) {
g.move = false;
if ( g.end !== false ) {
e.preventDefault();
e.stopPropagation();
g.tmp = g.target.closest( ".dbRollerC" );
eachItem = ( o.flipSizeOverride !== false ) ?
o.flipSizeOverride :
g.target[0].getBoundingClientRect().height;
w._offset(
g.tmp.data("field"),
( parseInt( ( g.start - g.end ) / ( eachItem ), 10 ) *
g.tmp.data( "amount" ) * g.direc ) );
}
g.start = false;
g.end = false;
} else {
g.move = false;
g.start = false;
g.end = false;
g.tmp = g.target.closest( ".dbRollerC" );
eachItem = ( o.flipSizeOverride !== false ) ?
o.flipSizeOverride :
g.target[0].getBoundingClientRect().height;
delta = ( -( g.velocity * 0.8 ) * Math.exp( -g.elapsed / 325 ) * 8 ) * -1;
currentPosition = parseInt( g.target.css( "marginTop" ).replace( /px/i, "" ), 10 );
goodPosition = parseInt( currentPosition + delta, 10 );
totalMove = g.pos - goodPosition;
numberFull = Math.round(totalMove / ( eachItem ));
goodGuess = numberFull * g.tmp.data( "amount" ) * g.direc;
g.target.animate(
{
marginTop : goodPosition
},
parseInt(10000/g.velocity) + 1000,
function() {
w._offset( g.tmp.data("field"), goodGuess );
}
);
e.preventDefault();
e.stopPropagation();
}
}
});
};