File: //var/www/lipovac/wp-content/plugins/the-events-calendar/src/resources/js/views/month-grid.js
/**
 * Makes sure we have all the required levels on the Tribe Object
 *
 * @since 4.9.4
 *
 * @type   {PlainObject}
 */
tribe.events = tribe.events || {};
tribe.events.views = tribe.events.views || {};
/**
 * Configures Month Grid Object in the Global Tribe variable
 *
 * @since 4.9.4
 *
 * @type  {PlainObject}
 */
tribe.events.views.monthGrid = {};
/**
 * Initializes in a Strict env the code that manages the Event Views
 *
 * @since 4.9.4
 *
 * @param  {PlainObject} $   jQuery
 * @param  {PlainObject} obj tribe.events.views.manager
 *
 * @return {void}
 */
( function( $, obj ) {
	'use strict';
	var $document = $( document );
	/**
	 * Selectors used for configuration and setup
	 *
	 * @since 4.9.4
	 *
	 * @type {PlainObject}
	 */
	obj.selectors = {
		grid: '[data-js="tribe-events-month-grid"]',
		row: '[data-js="tribe-events-month-grid-row"]',
		cell: '[data-js="tribe-events-month-grid-cell"]',
		focusable: '[tabindex]',
		focused: '[tabindex="0"]',
	};
	/**
	 * State data for month grid
	 *
	 * @since 4.9.4
	 *
	 * @type {PlainObject}
	 */
	obj.state = {
		grid: [],
		currentRow: 0,
		currentCol: 0,
	};
	/**
	 * Object of key codes
	 *
	 * @since 4.9.4
	 *
	 * @type {PlainObject}
	 */
	obj.keyCode = {
		END: 35,
		HOME: 36,
		LEFT: 37,
		UP: 38,
		RIGHT: 39,
		DOWN: 40,
	};
	/**
	 * Check if cell described by row and col is valid
	 *
	 * @since 4.9.4
	 *
	 * @param {integer} row row number of cell, 0 index
	 * @param {integer} col column number of cell, 0 index
	 *
	 * @return {boolean} true if cell is valid, false otherwise
	 */
	obj.isValidCell = function( row, col ) {
		return (
			! isNaN( row ) &&
			! isNaN( col ) &&
			row >= 0 &&
			col >= 0 &&
			obj.state.grid &&
			obj.state.grid.length &&
			row < obj.state.grid.length &&
			col < obj.state.grid[ row ].length
		);
	};
	/**
	 * Get next cell from current row, current column, and direction changes
	 *
	 * @since 4.9.4
	 *
	 * @param {integer} currentRow index of current row
	 * @param {integer} currentCol index of current column
	 * @param {integer} directionX number of steps to take in the X direction
	 * @param {integer} directionY number of steps to take in the Y direction
	 *
	 * @return {PlainObject} object containing next row and column indices
	 */
	obj.getNextCell = function( currentRow, currentCol, directionX, directionY ) {
		var row = currentRow + directionY;
		var col = currentCol + directionX;
		if ( obj.isValidCell( row, col ) ) {
			return {
				row: row,
				col: col,
			};
		}
		return {
			row: currentRow,
			col: currentCol,
		};
	};
	/**
	 * Set focus pointer to given row and column
	 *
	 * @since 4.9.4
	 *
	 * @param {integer} row index of row
	 * @param {integer} col index of column
	 *
	 * @return {boolean} boolean of whether focus pointer was set or not
	 */
	obj.setFocusPointer = function( row, col ) {
		if ( obj.isValidCell( row, col ) ) {
			$( obj.state.grid[ obj.state.currentRow ][ obj.state.currentCol ] ).attr( 'tabindex', '-1' );
			$( obj.state.grid[ row ][ col ] ).attr( 'tabindex', '0' );
			obj.state.currentRow = row;
			obj.state.currentCol = col;
			return true;
		}
		return false;
	};
	/**
	 * Focus cell at given row and column
	 *
	 * @since 4.9.4
	 *
	 * @param {integer} row index of row
	 * @param {integer} col index of column
	 *
	 * @return {void}
	 */
	obj.focusCell = function( row, col ) {
		if ( obj.setFocusPointer( row, col ) ) {
			$( obj.state.grid[ row ][ col ] ).focus();
		}
	};
	/**
	 * Handle keydown event to move focused grid cell
	 *
	 * @since 4.9.4
	 *
	 * @param {Event} event event object
	 *
	 * @return {void}
	 */
	obj.handleKeydown = function( event ) {
		var key = event.which || event.keyCode;
		var row = obj.state.currentRow;
		var col = obj.state.currentCol;
		var nextCell;
		switch ( key ) {
			case obj.keyCode.UP:
				nextCell = obj.getNextCell( row, col, 0, -1 );
				row = nextCell.row;
				col = nextCell.col;
				break;
			case obj.keyCode.DOWN:
				nextCell = obj.getNextCell( row, col, 0, 1 );
				row = nextCell.row;
				col = nextCell.col;
				break;
			case obj.keyCode.LEFT:
				nextCell = obj.getNextCell( row, col, -1, 0 );
				row = nextCell.row;
				col = nextCell.col;
				break;
			case obj.keyCode.RIGHT:
				nextCell = obj.getNextCell( row, col, 1, 0 );
				row = nextCell.row;
				col = nextCell.col;
				break;
			case obj.keyCode.HOME:
				if ( event.ctrlKey ) {
					row = 0;
				}
				col = 0;
				break;
			case obj.keyCode.END:
				if ( event.ctrlKey ) {
					row = obj.state.grid.length - 1;
				}
				col = obj.state.grid[ obj.state.currentRow ].length - 1;
				break;
			default:
				return;
		}
		obj.focusCell( row, col );
		event.preventDefault();
	};
	/**
	 * Handle click event to focus cell
	 *
	 * @since 4.9.4
	 *
	 * @param {Event} event event object
	 *
	 * @return {void}
	 */
	obj.handleClick = function( event ) {
		var $clickedCell = $( event.target ).closest( obj.selectors.focusable );
		for ( var row = 0; row < obj.state.grid.length; row++ ) {
			for ( var col = 0; col < obj.state.grid[ row ].length; col++ ) {
				if ( obj.state.grid[ row ][ col ].is( $clickedCell ) ) {
					obj.focusCell( row, col );
					return;
				}
			}
		}
	};
	/**
	 * Set up grid to state array
	 *
	 * @since 4.9.4
	 *
	 * @param {jQuery} $grid jQuery object of grid.
	 *
	 * @return {void}
	 */
	obj.setupGrid = function( $grid ) {
		$grid
			.find( obj.selectors.row )
			.each( function( rowIndex, row ) {
				var gridRow = [];
				$( row )
					.find( obj.selectors.cell )
					.each( function( colIndex, cell ) {
						var $cell = $( cell );
						// if cell is focusable (has tabindex attribute)
						if ( $cell.is( obj.selectors.focusable ) ) {
							// if cell is focusable and has tabindex of 0
							if ( $cell.is( obj.selectors.focused ) ) {
								obj.state.currentRow = obj.state.grid.length;
								obj.state.currentCol = gridRow.length;
							}
							// add focusable cell to gridRow
							gridRow.push( $cell );
						} else {
							var $focusableCell = $cell.find( obj.selectors.focusable );
							// if element is focusable (has tabindex attribute)
							if ( $focusableCell.is( obj.selectors.focusable ) ) {
								// if element is focusable and has tabindex of 0
								if ( $cell.is( obj.selectors.focused ) ) {
									obj.state.currentRow = obj.state.grid.length;
									obj.state.currentCol = gridRow.length;
								}
								// add focusable element to gridRow
								gridRow.push( $focusableCell );
							}
						}
					} );
				// add gridRow to grid if gridRow has focusable cells
				if ( gridRow.length ) {
					obj.state.grid.push( gridRow );
				}
			} );
	};
	/**
	 * Bind events for keydown and click on grid
	 *
	 * @since 4.9.4
	 *
	 * @param {jQuery} $grid jQuery object of grid.
	 *
	 * @return {void}
	 */
	obj.bindEvents = function( $grid ) {
		$grid
			.on( 'keydown', obj.handleKeydown )
			.on( 'click', obj.handleClick );
	};
	/**
	 * Initialize grid.
	 *
	 * @since 4.9.4
	 *
	 * @param {Event}   event      JS event triggered.
	 * @param {integer} index      jQuery.each index param from 'afterSetup.tribeEvents' event.
	 * @param {jQuery}  $container jQuery object of view container.
	 * @param {object}  data       data object passed from 'afterSetup.tribeEvents' event.
	 *
	 * @return {void}
	 */
	obj.init = function( event, index, $container, data ) {
		var $grid = $container.find( obj.selectors.grid );
		obj.setupGrid( $grid );
		obj.setFocusPointer( obj.state.currentRow, obj.state.currentCol );
		obj.bindEvents( $grid );
	};
	/**
	 * Handles the initialization of the multiday events when Document is ready
	 *
	 * @since 4.9.4
	 *
	 * @return {void}
	 */
	obj.ready = function() {
		$document.on( 'afterSetup.tribeEvents', tribe.events.views.manager.selectors.container, obj.init );
		/**
		 * @todo: do below for ajax events
		 */
		// on 'beforeAjaxBeforeSend.tribeEvents' event, remove all listeners
		// on 'afterAjaxError.tribeEvents', add all listeners
	};
	// Configure on document ready
	$document.ready( obj.ready );
} )( jQuery, tribe.events.views.monthGrid );