/** * JTSage-DateBox * @fileOverview Provides the calbox mode * @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({ calHighToday : true, calHighPick : true, calHighOutOfBounds : true, calSelectedOutOfBounds : true, calShowDays : true, calOnlyMonth : false, calShowWeek : false, calUsePickers : false, calNoHeader : false, calYearPickMin : -6, calYearPickMax : 6, calYearPickRelative : true, calFormatter : false, calBeforeAppendFunc : function(t) { return t; }, highDays : false, highDates : false, highDatesRec : false, highDatesPeriod : false, highDatesAlt : false, enableDates : false, calDateList : false, calShowDateList : false }); /** * Return first date of visible calendar grid * * @return {object} JavaScript Date Object */ JTSageDateBox.getCalStartGrid = function () { return this.firstOfGrid; }; /** * Return last date of visible calendar grid * * @return {object} JavaScript Date Object */ JTSageDateBox.getCalEndGrid = function() { return this.lastOfGrid; }; /** * Is the currently selected date in the visible calendar grid? * * @return {boolean} True = yes it is */ JTSageDateBox.isSelectedInCalGrid = function() { var w = this; if ( w.firstOfGrid === false || w.lastOfGrid === false ) { return false; } return ( w.firstOfGrid.comp() <= w.originalDate.comp() && w.originalDate.comp() <= w.lastOfGrid.comp() ); }; JTSageDateBox.isSelectedInBounds = function() { var w = this; if ( w.firstOfMonth === false || w.lastOfMonth === false ) { return false; } return ( w.firstOfMonth.comp() <= w.originalDate.comp() && w.originalDate.comp() <= w.lastOfMonth.comp() ); }; /** * Is the specified date in the visible calendar grid? * * @param {object} JavaScript Date Object * @return {boolean} True = yes it is */ JTSageDateBox.isInCalGrid = function ( date ) { var w = this; if ( w.firstOfGrid === false || w.lastOfGrid === false ) { return false; } return ( w.firstOfGrid.comp() <= date.comp() && date.comp() <= w.lastOfGrid.comp() ); }; /** * @typedef {Object} _cal_ThemeDate_Return * @property {string} theme Theme class to use * @property {boolean} inBounds Date appears in the "current" month */ /** * Apply theme information for a date * * @param {object} javascript Date Object * @param {number} dispMonth Current month of display * @return {_cal_ThemeDate_Return} Theme information */ JTSageDateBox._cal_ThemeDate = function( testDate, dispMonth ) { /* Newest Version of the date checker. With less mess? */ /* * Returns an object: * * object.theme = Theme to use for the date */ var w = this, o = this.options, itt, done = false, returnObject = { theme : o.theme_cal_Default, inBounds : true }, dateThemes = [ [ "selected", "theme_cal_Selected" ], [ "today" , "theme_cal_Today" ], [ "highDates", "theme_cal_DateHigh" ], [ "highDatesAlt", "theme_cal_DateHighAlt" ], [ "highDatesRec", "theme_cal_DateHighRec" ], [ "highDatesPeriod", "theme_cal_DateHighRec" ], [ "highDays", "theme_cal_DayHigh" ] ]; w.realToday = new w._date(); // Shortcut if out of bounds, no other theme can apply. if ( testDate.get(1) !== dispMonth ) { returnObject.inBounds = false; if ( o.calHighOutOfBounds !== false ) { returnObject.theme = o.theme_cal_OutOfBounds; done = true; // Allow only selected theme if specifly enabled. if ( o.calSelectedOutOfBounds !== false && w._ThemeDateCK.selected.call( w, testDate ) ) { returnObject.theme = o.theme_cal_Selected; } } } for ( itt = 0; itt < dateThemes.length && !done; itt++ ) { if ( w._ThemeDateCK[ dateThemes[ itt ][0] ].call( w, testDate ) ) { returnObject.theme = o[ dateThemes[ itt ][1] ]; done = true; } } return returnObject; }; /** * Build the calbox * * @memberOf JTSageDateBox._build * @this JTSageDateBox */ JTSageDateBox._build.calbox = function () { var w = this, i, o = this.options, // Today's real date, not based on selection date_realToday = new w._date(), // First day of the displayed month date_firstOfMonth = w.theDate.copy( false, [ 0, 0, 1, 12, 1, 1, 1 ] ), // Last day of the displayed month date_lastOfMonth = date_firstOfMonth.copy( [ 0, 1 ] ).adj( 2, -1 ), // Actual month (used to find bounds) date_displayMonth = date_firstOfMonth.get(1), // Actual year (used to find bounds) date_displayYear = date_firstOfMonth.get(0), // How many days from the previous month (or blanks) need to appear grid_headOffset = w.__( "calStartDay" ) - date_firstOfMonth.getDay(), // How many days from the next month (of blanks) need to appear grid_tailOffset = 6 + ( w.__( "calStartDay" ) - date_lastOfMonth.getDay()), // First date of the grid (possibly blanked out) date_firstOfGrid = date_firstOfMonth.copy( [ 0, 0, grid_headOffset ] ), // Last date of the grid (possibly blanked out) date_lastOfGrid = date_lastOfMonth.copy( [ 0, 0, grid_tailOffset ] ), // How many weeks the grid has (Low 4, Max 6.) grid_Weeks = ( date_lastOfGrid.getEpochDays() - date_firstOfGrid.getEpochDays() + 1 ) / 7, // The current working date. Stepped from first to last date of the grid date_working = date_firstOfGrid.copy(), // How many columns the grid has (7 days, 8th optional week number) grid_Cols = o.calShowWeek ? 8 : 7, // This holds the grid temporarily calContent = "", calCntlRow = "", // Control variables. cntlRow, cntlCol, cntlObj = {}, // This holds the day of week display weekdayControl = ""; // Set up some info to pull from calbox if needed. w.firstOfGrid = date_firstOfGrid; w.lastOfGrid = date_lastOfGrid; w.firstOfMonth = date_firstOfMonth; w.lastOfMonth = date_lastOfMonth; // Clear internal widget HTML, if not already empty. if ( typeof w.d.intHTML !== "boolean" ) { w.d.intHTML.remove(); w.d.intHTML = null; } // Attempt to grab associated label, fallback to i18n string if none found w.d.headerText = w._grabLabel( w.__( "titleDateDialogLabel" ) ); w.d.intHTML = $( "<span>" ); w.d.intHTML.addClass( o.theme_spanStyle ); // Internal header (not the widget master header, a header for the calendar) // // Expects a ".dbCalNext" and ".dbCalPrev" for prev/next button events. if ( o.calNoHeader === false ) { calContent = w.style_pnHead( w._formatter( w.__( "calHeaderFormat"), w.theDate ), ( w.__( "isRTL" ) === true ) ? o.theme_cal_NextBtn : o.theme_cal_PrevBtn, ( w.__( "isRTL" ) === true ) ? o.theme_cal_PrevBtn : o.theme_cal_NextBtn, "dbCalPrev", "dbCalNext" ); if ( w.__( "isRTL" ) === true ) { calContent.children().each( function( i, item ) { calContent.prepend( item ); }); } calContent.appendTo( w.d.intHTML ); w.d.intHTML .on( o.clickEvent, ".dbCalNext", function(e) { e.preventDefault(); e.stopPropagation(); if ( w.theDate.getDate() > 28 ) { w.theDate.setDate(1); } w._offset( "m", 1 ); return false; }) .on( o.clickEvent, ".dbCalPrev", function(e) { e.preventDefault(); e.stopPropagation(); if ( w.theDate.getDate() > 28 ) { w.theDate.setDate(1); } w._offset( "m", -1 ); return false; }); } // Picker controls, if enabled. if ( o.calUsePickers !== false ) { calContent = w.style_picker( w._pickRanges( date_displayMonth, date_displayYear, date_realToday.get(0), o.calYearPickRelative ), o.theme_cal_Pickers, "dbCalPickMonth", "dbCalPickYear" ); if ( w.__( "isRTL" ) === true ) { calContent.children().each( function( i, item ) { calContent.prepend( item ); }); } calContent.appendTo( w.d.intHTML ); w.d.intHTML.on( "change", "#dbCalPickMonth, #dbCalPickYear", function() { if ( w.theDate.get(2) > 28 ) { w.theDate.setD( 2, 1 ); //Set first of month if over the 28th. } w.theDate.setD( 1, $( "#dbCalPickMonth" ).val() ); // Set choosen month w.theDate.setD( 0, $( "#dbCalPickYear" ).val() ); // Set choosen year w._t( { method : "displayChange", selectedDate : w.originalDate, shownDate : w.theDate, thisChange : "p", thisChangeAmount : null, gridStart : w.firstOfGrid, gridEnd : w.lastOfGrid, selectedInGrid : w.isSelectedInCalGrid(), selectedInBounds : w.isSelectedInBounds() }); w.refresh(); }); } // The actual grid system. calContent = $( w.style_calGrid() ).appendTo( w.d.intHTML ).find( ".dbCalGrid" ).first(); if ( o.calShowDays ) { w._cal_days = w.__( "daysOfWeekShort").concat( w.__( "daysOfWeekShort" ) ); weekdayControl = w.style_calRow(); if ( o.calShowWeek ) { weekdayControl.append( w.style_calTxt( " ", false, 8 ) ); } for ( i=0; i<=6;i++ ) { weekdayControl.append( w.style_calTxt( w._cal_days[ ( i + w.__( "calStartDay") ) % 7 ], true, grid_Cols ) ); } weekdayControl.appendTo( calContent ); if ( w.__( "isRTL" ) === true ) { weekdayControl.children().each( function( i, item ) { weekdayControl.prepend( item ); } ); } } // if options.calFormatter is just a string, attempt to pull it as a function // reference from the global namespace o.calFormatter = w._prepFunc( o.calFormatter ); // if options.calBeforeAppendFunc is just a string, attempt to pull it as a // function reference from the global namespace o.calBeforeAppendFunc = w._prepFunc( o.calBeforeAppendFunc ); /* Actually build and populate the calendar. One pass */ for ( cntlRow = 0; cntlRow < grid_Weeks; cntlRow++ ) { // Begin:: ROW calCntlRow = w.style_calRow(); // Show week numbers if ( o.calShowWeek ) { calCntlRow.append( w.style_calTxt( date_working.getDWeek(4), false, 8 ) ); } for ( cntlCol = 0; cntlCol < 7; cntlCol++ ) { // Each: Date // Check and theme date. cntlObj = Object.assign( w._newDateChecker( date_working ), w._cal_ThemeDate( date_working, date_displayMonth ) ); // Format the display date cntlObj.displayText = ( o.calFormatter === false ) ? cntlObj.dateObj.get(2) : o.calFormatter.call( w, cntlObj ); // Create HTML // // Event "button" MUST have the class of ".dbEvent" cntlObj.htmlObj = w.style_calBtn( cntlObj, grid_Cols ); // Add data object to event object cntlObj.eventObj = cntlObj.htmlObj.find( ".dbEvent" ).first(); cntlObj.eventObj.data( cntlObj ); // Run the before append function if ( o.calBeforeAppendFunc !== false ) { cntlObj = o.calBeforeAppendFunc.call( w, cntlObj ); } // Add to row control object if ( o.calOnlyMonth === false || cntlObj.inBounds === true ) { calCntlRow.append( cntlObj.htmlObj ); } else { calCntlRow.append( w.style_calTxt( " ", false, grid_Cols ) ); } //Add ONE day date_working.adj( 2, 1 ); } // Deal with RTL languages (flex is easiest) if ( w.__( "isRTL" ) === true ) { calCntlRow.children().each( function( i, item ) { calCntlRow.prepend( item ); }); } // Add row to grid. calCntlRow.appendTo( calContent ); } // Quick Date Picker if turned on. if ( o.calShowDateList !== false && o.calDateList !== false ) { w.style_dateList( w.__( "calDateListLabel" ), o.calDateList, o.theme_cal_DateList, "dbCalPickList" ).appendTo(w.d.intHTML); w.d.intHTML.on( "change", "#dbCalPickList", function() { var iPut = $(this).val().split( "-" ); w.theDate = new w._date( iPut[0], iPut[1] - 1, iPut[2], 12, 1, 1, 1 ); w._t( { method : "doset" } ); w._t( { method : "dorefresh" } ); w._t( { method : "close" } ); }); } // Bottom Buttons w.d.intHTML.append( w._doBottomButtons.call( w, false ) ); // Each date event loop, swipe and mouse events. w.d.intHTML .on( o.clickEvent, ".dbEvent", function(e) { e.preventDefault(); if ( $( this ).data( "good" ) ) { w.theDate = $( this ).data( "dateObj" ).copy(); w._t( { method : "set", value : w._formatter( w.__fmt(),w.theDate ), date : w.theDate } ); if ( o.displayMode === "inline" ) { w._t( { method : "dorefresh" } ); } w._t( { method : "close" } ); } }) .on( w.wheelEvent, function(e,d) { e.preventDefault(); d = ( typeof d === "undefined" ) ? Math.sign(e.originalEvent.wheelDelta) : d; w.theDate.setD( 2, 1 ); w._offset( "m", ( d > 0 ) ? 1 : -1 ); }); };