spivurno
4/18/2015 - 12:54 PM

Gravity Wiz // Gravity Forms // Date Range Display

Gravity Wiz // Gravity Forms // Date Range Display

<?php
/**
 * Gravity Wiz // Gravity Forms // Date Range Display
 *
 * Display a group of color-coded date ranges on a calendar based on a GF selected date.
 *
 * @version	  1.0
 * @author    David Smith <david@gravitywiz.com>
 * @license   GPL-2.0+
 * @link      http://gravitywiz.com/...
 */
class GW_Date_Range_Display {

	protected static $is_script_output = false;

	public function __construct( $args = array() ) {

		// set our default arguments, parse against the provided arguments, and store for use throughout the class
		$this->_args = wp_parse_args( $args, array(
			'selected_date'    => false, // '1987-01-13'
			'target_id'        => false, // '#date-range-display'
			'form_id'          => false, // 123
			'source_field_id'  => false, // 12,
			'sequential'       => false,
			'start_from_last'  => false,
			'ranges'           => array()
		) );

		// do version check in the init to make sure if GF is going to be loaded, it is already loaded
		add_action( 'init', array( $this, 'init' ) );

	}

	function init() {

		// make sure we're running the required minimum version of Gravity Forms
		if( ! property_exists( 'GFCommon', 'version' ) || ! version_compare( GFCommon::$version, '1.8', '>=' ) ) {
			return;
		}

		// time for hooks
		add_action( 'gform_enqueue_scripts', array( $this, 'enqueue_form_scripts' ) );
		add_filter( 'gform_pre_render', array( $this, 'load_form_script' ) );
		add_filter( 'gform_register_init_scripts', array( $this, 'add_init_script' ) );

	}

	function enqueue_form_scripts( $form ) {

		if( $this->is_applicable_form( $form ) ) {
			wp_enqueue_script( 'gform_conditional_logic' );
		}

	}

	function load_form_script( $form ) {

		if( $this->is_applicable_form( $form ) && ! self::$is_script_output ) {
			$this->output_script();
		}

		return $form;
	}

	function output_script() {
		?>

		<script type="text/javascript">

			( function( $ ) {

				window.GWDateRangeDisplay = function( args ) {

					var self = this;

					// copy all args to current object: (list expected props)
					for( prop in args ) {
						if( args.hasOwnProperty( prop ) )
							self[prop] = args[prop];
					}

					self.init = function() {

						self.$dateField = $( '#input_' + self.formId + '_' + self.sourceFieldId );
						self.$calendarElem = $( self.targetId );

						self.$calendarElem.datepicker( {
							numberOfMonths: 1,
							dayNamesMin: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
							beforeShowDay: function( date ) {

								var cssClass  = '';

								for( var i = 0; i < self.ranges.length; i++ ) {

									if( ! self.isRangeActive( self.ranges[i] ) ) {
										continue;
									}

									var baseDate  = self.sequential && endDate ? endDate : self.getSelectedDate(),
										startDate = self.addDaysToDate( new Date( baseDate ), self.ranges[i].start ),
										endDate   = self.addDaysToDate( new Date( baseDate ), self.ranges[i].end ),
										inRange   = date >= startDate && date <= endDate;

									if( inRange && ! self.isWeekend( date ) ) {
										cssClass = 'range' + ( i + 1 );
									}

								}

								return [ true, cssClass, '' ];
							}
						} ).addClass( 'gw-date-range-display' );

						self.$dateField.on( 'change', function() {
							GWDateRangeDisplay.refresh();
						} );

						self.bindConditionalLogic();

						GWDateRangeDisplay.refresh();

					};

					GWDateRangeDisplay.refresh = function( date ) {

						if( typeof date == 'undefined' ) {
							date = self.getSelectedDate();
						}

						self.$calendarElem.datepicker( 'setDate', date );
						self.$calendarElem.datepicker( 'option', 'numberOfMonths', self.getMonthDisplayCount( date ) );

						self.buildCalendarKey();

					};

					self.getSelectedDate = function() {

						var dateValue    = self.$dateField.val(),
							selectedDate = dateValue ? new Date( dateValue ) : new Date(),
							maxRange     = self.getMaxRange( selectedDate ),
							startingDate = selectedDate,
							dayCount     = 0;

						if( self.startFromLast ) {

							while( maxRange.end > maxRange.start ) {
								if( ! self.isWeekend( maxRange.end ) ) {
									dayCount++;
								}
								maxRange.end.setDate( maxRange.end.getDate() - 1 );
							}

							startingDate = self.subtractDaysFromDate( startingDate, dayCount );

						} else {

							if( self.isWeekend( startingDate ) ) {
								startingDate = self.addDaysToDate( startingDate, 1 );
							}

						}

						startingDate.setHours( 0, 0, 0, 0 );

						return startingDate;
					};

					self.addDaysToDate = function( fromDate, days ) {
						var count = 0;
						while( count < days ) {
							fromDate.setDate( fromDate.getDate() + 1 );
							if ( ! self.isWeekend( fromDate ) ) {
								count++;
							}
						}
						return fromDate;
					};

					self.subtractDaysFromDate = function( fromDate, days ) {
						while( days > 0 ) {
							fromDate.setDate( fromDate.getDate() - 1 );
							if ( ! self.isWeekend( fromDate ) ) {
								days--;
							}
						}
						return fromDate;
					};

					self.isWeekend = function( date ) {
						return date.getDay() === 0 || date.getDay() === 6;
					};

					self.getMaxRange = function( selectedDate ) {

						var earliestDate, latestDate, maxRange;

						for( var i = 0; i < self.ranges.length; i++ ) {

							if( ! self.isRangeActive( self.ranges[i] ) ) {
								continue;
							}

							var baseDate  = self.sequential && endDate ? endDate : selectedDate,
								startDate = self.addDaysToDate( new Date( baseDate ), self.ranges[i].start ),
								endDate   = self.addDaysToDate( new Date( baseDate ), self.ranges[i].end );

							if( ! earliestDate || earliestDate >= startDate ) {
								earliestDate = startDate;
							}

							if( ! latestDate || latestDate <= endDate ) {
								latestDate = endDate;
							}

						}

						maxRange = {
							start: earliestDate,
							end:   latestDate
						};

						return maxRange;
					};

					self.getMonthDisplayCount = function( selectedDate ) {

						var maxRange   = self.getMaxRange( selectedDate ),
							startMonth = maxRange.start.getMonth(),
							endMonth   = maxRange.end.getMonth();

						if( endMonth < startMonth ) {
							endMonth += 11;
						}

						return ( endMonth - startMonth ) + 1;
					};

					self.buildCalendarKey = function() {

						var markup = '',
							styles = '';

						for( var i = 0; i < self.ranges.length; i++ ) {

							var properIndex = i + 1;

							// always add style
							styles += '.gw-date-range-display td.range' + properIndex + ', .gw-date-range-display .gwdrd-range-key.range' + properIndex + ':before { background-color: ' + self.ranges[i].color + ' !important; }';

							// only add markup for active ranges
							if( self.isRangeActive( self.ranges[i] ) ) {
								markup += '<li class="gwdrd-range-key range' + properIndex + '">' + self.ranges[i].label + '</li>';
							}

						}

						var $keyElem   = $( '.gwdrd-range-keys' ),
							$styleElem = $( '#gwdrd-styles' );

						if( $keyElem.length <= 0 ) {
							$keyElem = $( '<ul class="gwdrd-range-keys"></ul>' );
							self.$calendarElem.prepend( $keyElem );
						}

						$keyElem.html( markup );

						if( $styleElem.length <= 0 ) {
							$styleElem = $( '<style type="text/css">' + styles + '</style>' );
							$( 'head' ).append( $styleElem );
						}



					};

					self.isRangeActive = function( range ) {
						var hasConditionalLogic = typeof range.conditionalLogic != 'undefined';
						return ( hasConditionalLogic && gf_get_field_action( self.formId, range.conditionalLogic ) == 'show' ) || ! hasConditionalLogic;
					};

					self.bindConditionalLogic = function() {

						var boundFields = [];

						for( var i = 0; i < self.ranges.length; i++ ) {

							var range = self.ranges[i];

							if( ! range['conditionalLogic'] ) {
								continue;
							}

							for( var j = 0; j < range.conditionalLogic.rules.length; j++ ) {

								var rule = range.conditionalLogic.rules[j];

								if( $.inArray( rule.fieldId, boundFields ) == -1 ) {
									$( 'li#field_' + self.formId + '_' + rule.fieldId ).find( 'input, select, textarea' ).change( function() {
										GWDateRangeDisplay.refresh();
									} );
									boundFields.push( rule.fieldId );
								}

							}

						}

					};

					self.init();

				}

			} )( jQuery );

		</script>

		<style type="text/css">
			.gw-date-range-display .ui-datepicker {
				width: 100% !important;
				border: 0;
				box-shadow: 0 0 0;
			}
			.gw-date-range-display .ui-datepicker-prev,
			.gw-date-range-display .ui-datepicker-next {
				display: none;
			}
			.gw-date-range-display .ui-datepicker-multi .ui-datepicker-group {
				float: none;
				margin: 0 0 1rem;
				width: 100%;
			}
			.gw-date-range-display .ui-datepicker .ui-datepicker-header {
				border: 0;
				padding: 0;
				background: transparent;
				color: #000;
				text-shadow: none;
				filter: none;
			}
			.gw-date-range-display .ui-datepicker .ui-datepicker-title {
				margin: 0;
				text-align: left;
				font-size: 12px;
				text-shadow: none !important;
				background: transparent;
				filter: none;
			}
			.gw-date-range-display .ui-datepicker-multi .ui-datepicker-group table {
				width: 100%;
				margin: 0;
			}
			.gw-date-range-display .ui-datepicker thead {
				background: transparent;
			}
			.gw-date-range-display .ui-datepicker th {
				border: 1px solid #919191;
				font-size: 10px;
				text-transform: uppercase;
				font-weight: normal;
				border-bottom: 1px solid #000;
				text-shadow: none;
				color: #FFF;
				background: rgba(0,0,0,.5);
				filter: none !important;
			}
			.gw-date-range-display .ui-datepicker-calendar tbody td,
			.gw-date-range-display .ui-state-disabled {
				border: 1px solid #919191;
				border-width: 0 1px 1px 0;
				background: rgba(255,255,255,.35);
				font-size: 12px;
				height: 34px;
				width: 14%;
				padding-top:4px !important;
				padding-bottom:4px !important;
				text-align: center;
			}
			.gw-date-range-display .ui-datepicker-calendar tbody td:first-child {
				border-left: 1px solid #919191;;
			}
			.gw-date-range-display .ui-state-default,
			.gw-date-range-display .ui-widget-content .ui-state-default,
			.gw-date-range-display .ui-widget-header .ui-state-default {
				background: transparent;
				border: 0;
				text-align: center;
				box-shadow: 0 0 0;
				color: #000;
				text-shadow: none;
				cursor: default;
				filter: none !important;
			}

			body .gform_wrapper form div.gform_body ul.gform_fields li.gfield.gfield_html ul li.gwdrd-range-key {
				list-style: none !important;
				line-height: 22px;
				margin: 0 0 12px;
				text-transform: uppercase;
				font-size: 11px;
			}
			body .gform_wrapper form div.gform_body ul.gform_fields li.gfield.gfield_html ul li.gwdrd-range-key:before {
				content: '';
				display: block;
				float: left;
				width: 20px;
				height: 20px;
				border: 1px solid #000;
				margin-right: 8px;
			}

			.ui-datepicker-title {
				font-size:1.75em !important;
				line-height: 1.35em;
				color: #adcfdf;
				padding-top:10px;
				padding-left:3px;
				padding-bottom:5px;
				border: 0px !important;
			}

			.gwdrd-range-keys {
				background:rgba(255,255,255,.15);
				padding:15px 15px 10px 15px !important;
			}

			.gw-date-range-display .ui-datepicker .ui-datepicker-header {
				-webkit-box-shadow: none;
				-moz-box-shadow: none;
				box-shadow: none;
				text-shadow: none;
				filter: none;
			}

		</style>

		<?php

		self::$is_script_output = true;

	}

	function add_init_script( $form ) {

		if( ! $this->is_applicable_form( $form ) ) {
			return;
		}

		$args = array(
			'formId'        => $this->_args['form_id'],
			'sourceFieldId' => $this->_args['source_field_id'],
			'targetId'      => $this->_args['target_id'],
			'sequential'    => $this->_args['sequential'],
			'startFromLast' => $this->_args['start_from_last'],
			'ranges'        => $this->_args['ranges']
		);

		$script = 'new GWDateRangeDisplay( ' . json_encode( $args ) . ' );';
		$slug   = implode( '_', array( 'gw_date_range_display', $this->_args['form_id'], $this->_args['source_field_id'] ) );

		GFFormDisplay::add_init_script( $this->_args['form_id'], $slug, GFFormDisplay::ON_PAGE_RENDER, $script );

	}

	function is_applicable_form( $form ) {

		$form_id = isset( $form['id'] ) ? $form['id'] : $form;

		return $form_id == $this->_args['form_id'];
	}

}

# Configuration

new GW_Date_Range_Display( array(
	'target_id'        => '#gw-display-calendar',
	'form_id'          => 812,
	'source_field_id'  => 12,
	'sequential'       => true,
	'start_from_last'  => true,
	'ranges' => array(
		array(
			'label' => 'Final Approvals Due',
			'color' => '#ED1D24',
			'start' => 0,
			'end'   => 0
		),
		array(
			'label' => __( 'Translating' ),
			'color' => '#b4f1fa',
			'start' => 1,
			'end'   => 10,
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 14,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#b2df97',
			'label' => __( 'Printing' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 13,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#d1edbf',
			'label' => __( 'Shipping' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 13,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 1,
			'color' => '#ffdf2e',
			'label' => __( 'In-Hand Due Date' )
		)
	)
) );

# Configuration

new GW_Date_Range_Display( array(
	'target_id'        => '#gw-guess-calendar',
	'form_id'          => 5,
	'source_field_id'  => 1,
	'sequential'       => true,
	'start_from_last'  => true,
	'ranges' => array(
		array(
			'label' => 'Final Approvals Due',
			'color' => '#ED1D24',
			'start' => 0,
			'end'   => 0
		),
		array(
			'label' => __( 'Translating' ),
			'color' => '#b4f1fa',
			'start' => 1,
			'end'   => 10,
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 8,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#b2df97',
			'label' => __( 'Printing' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 7,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#d1edbf',
			'label' => __( 'Shipping' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 7,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 1,
			'color' => '#ffdf2e',
			'label' => __( 'In-Hand Due Date' )
		)
	)
) );

new GW_Date_Range_Display( array(
	'target_id'        => '#gw-guess-calendar',
	'form_id'          => 10,
	'source_field_id'  => 1,
	'sequential'       => true,
	'start_from_last'  => true,
	'ranges' => array(
		array(
			'label' => 'Final Approvals Due',
			'color' => '#ED1D24',
			'start' => 0,
			'end'   => 0
		),
		array(
			'label' => __( 'Translating' ),
			'color' => '#b4f1fa',
			'start' => 1,
			'end'   => 10,
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 8,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#b2df97',
			'label' => __( 'Printing' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 7,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 5,
			'color' => '#d1edbf',
			'label' => __( 'Shipping' ),
			'conditionalLogic' => array(
				'actionType' => 'show',
				'logicType'  => 'all',
				'rules'      => array(
					array(
						'fieldId'  => 7,
						'operator' => 'is',
						'value'    => 'Yes'
					)
				)
			)
		),
		array(
			'start' => 1,
			'end'   => 1,
			'color' => '#ffdf2e',
			'label' => __( 'In-Hand Due Date' )
		)
	)
) );