Skip to content

Instantly share code, notes, and snippets.

@spivurno
Last active December 16, 2021 15:22
Show Gist options
  • Save spivurno/718c3dd804d36a740a4a to your computer and use it in GitHub Desktop.
Save spivurno/718c3dd804d36a740a4a to your computer and use it in GitHub Desktop.
Gravity Wiz // Gravity Forms // Rounding by Increment
<?php
/**
* WARNING! THIS SNIPPET MAY BE OUTDATED.
* The latest version of this snippet can be found in the Gravity Wiz Snippet Library:
* https://github.com/gravitywiz/snippet-library/blob/master/gravity-forms/gw-gravity-forms-rounding.php
*/
/**
* Gravity Wiz // Gravity Forms // Rounding by Increment
*
* Provides a variety of rounding functions for Gravity Form Number fields powered by the CSS class setting for each field. Functions include:
*
* + rounding to an increment (i.e. increment of 100 would round 1 to 100, 149 to 100, 150 to 200, etc) | class: 'gw-round-100'
* + rounding up by an increment (i.e. increment of 50 would round 1 to 50, 51 to 100, 149 to 150, etc) | class: 'gw-round-up-50'
* + rounding down by an increment (i.e. increment of 25 would round 1 to 0, 26 to 25, 51 to 50, etc) | class: 'gw-round-down-25'
* + rounding up to a specific minimum (i.e. min of 50 would round 1 to 50, 51 and greater would not be rounded) | class: 'gw-round-min-50'
* + rounding down to a specific maximum (i.e. max of 25 would round 26 to 25, 25 and below would not be rounded) | class: 'gw-round-max-25'
*
* @version 1.11
* @author David Smith <[email protected]>
* @license GPL-2.0+
* @link http://gravitywiz.com/rounding-increments-gravity-forms/
*
* Plugin Name: Gravity Forms Rounding
* Plugin URI: http://gravitywiz.com/rounding-increments-gravity-forms/
* Description: Round your field values (including calculations) up, down, by an increment, or to a specific minimum or maximum value.
* Author: Gravity Wiz
* Version: 1.11
* Author URI: http://gravitywiz.com
*/
class GW_Rounding {
private static $instance = null;
protected static $is_script_output = false;
protected $class_regex = 'gw-round-(\w+)-?(\w+)?';
public static function get_instance() {
if( null == self::$instance )
self::$instance = new self;
return self::$instance;
}
private function __construct( $args = array() ) {
// 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_filter( 'gform_pre_render', array( $this, 'prepare_form_and_load_script' ), 10, 2 );
add_filter( 'gform_register_init_scripts', array( $this, 'add_init_script' ) );
add_filter( 'gform_enqueue_scripts', array( $this, 'enqueue_form_scripts' ) );
add_action( 'gform_pre_submission', array( $this, 'override_submitted_value' ), 10, 5 );
add_filter( 'gform_calculation_result', array( $this, 'override_submitted_calculation_value' ), 10, 5 );
}
public function prepare_form_and_load_script( $form, $is_ajax_enabled ) {
if( ! $this->is_applicable_form( $form ) ) {
return $form;
}
if( $this->is_applicable_form( $form ) && ! has_action( 'wp_footer', array( $this, 'output_script' ) ) ) {
add_action( 'wp_footer', array( $this, 'output_script' ) );
add_action( 'gform_preview_footer', array( $this, 'output_script' ) );
add_action( 'admin_footer', array( $this, 'output_script' ) );
}
foreach( $form['fields'] as &$field ) {
if( preg_match( $this->get_class_regex(), $field['cssClass'] ) ) {
$field['cssClass'] .= ' gw-rounding';
}
}
return $form;
}
public function is_ajax_submission( $form_id, $is_ajax_enabled ) {
return class_exists( 'GFFormDisplay' ) && isset( GFFormDisplay::$submission[ $form_id ] ) && $is_ajax_enabled;
}
function output_script() {
?>
<script type="text/javascript">
var GWRounding;
( function( $ ) {
GWRounding = 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.fieldElems = $( '#gform_wrapper_' + self.formId + ' .gw-rounding' );
self.parseElemActions( self.fieldElems );
self.bindEvents();
};
self.parseElemActions = function( elems ) {
elems.each( function() {
var cssClasses = $( this ).attr( 'class' ),
roundingActions = self.parseActions( cssClasses );
$( this ).data( 'gw-rounding', roundingActions );
} );
};
self.parseActions = function( str ) {
var matches = getMatchGroups( String( str ), new RegExp( self.classRegex.replace( /\\/g, '\\' ), 'i' ) ),
roundingActions = [];
for( var i = 0; i < matches.length; i++ ) {
var action = matches[i][1],
actionValue = matches[i][2];
if( typeof actionValue == 'undefined' ) {
actionValue = action;
action = 'round';
}
var roundingAction = {
'action': action,
'actionValue': actionValue
};
roundingActions.push( roundingAction );
}
return roundingActions;
};
self.bindEvents = function() {
self.fieldElems.each( function() {
var $targets;
// Attempt to target only the quantity input of a Single Product field.
if( $( this ).hasClass( 'gfield_price' ) ) {
$targets = $( this ).find( '.ginput_quantity' );
}
// Otherwise, target all inputs of the given field.
if( ! $targets.length ) {
$targets = $( this ).find( 'inputs' );
}
$targets.each( function() {
self.applyRoundingActions( $( this ) );
} ).blur( function() {
self.applyRoundingActions( $( this ) );
} );
} );
gform.addFilter( 'gform_calculation_result', function( result, formulaField, formId, calcObj ) {
var $input = $( '#input_' + formId + '_' + formulaField.field_id )
$field = $input.parents( '.gfield' );
if( $field.hasClass( 'gw-rounding' ) ) {
result = self.getRoundedValue( $input, result );
}
return result;
} );
};
self.applyRoundingActions = function( $input ) {
var value = self.getRoundedValue( $input );
if( $input.val() != value ) {
$input.val( value ).change();
}
};
self.getRoundedValue = function( $input, value ) {
var $field = $input.parents( '.gfield' ),
actions = $field.data( 'gw-rounding' );
// allows setting the 'gw-rounding' data for an element to null and it will be reparsed
if( actions === null ) {
self.parseElemActions( $field );
actions = $field.data( 'gw-rounding' );
}
if( typeof actions == 'undefined' || actions === false || actions.length <= 0 ) {
return;
}
if( typeof value == 'undefined' ) {
value = gformToNumber( $input.val() );
var currency = new Currency(gf_global.gf_currency_config);
value = gformCleanNumber( value, currency.currency.symbol_right, currency.currency.symbol_left, currency.currency.decimal_separator );
}
if( value != '' ) {
for( var i = 0; i < actions.length; i++ ) {
value = GWRounding.round( value, actions[i].actionValue, actions[i].action );
}
}
return isNaN( value ) ? '' : value;
};
GWRounding.round = function( value, actionValue, action ) {
var interval, base, min, max;
value = parseFloat( value );
actionValue = parseInt( actionValue );
switch( action ) {
case 'min':
min = actionValue;
if( value < min ) {
value = min;
}
break;
case 'max':
max = actionValue;
if( value > max ) {
value = max;
}
break;
case 'up':
interval = actionValue;
base = Math.ceil( value / interval );
value = base * interval;
break;
case 'down':
interval = actionValue;
base = Math.floor( value / interval );
value = base * interval;
break;
default:
interval = actionValue;
base = Math.round( value / interval );
value = base * interval;
break;
}
return parseInt( value );
};
self.init();
}
} )( jQuery );
</script>
<?php
}
function add_init_script( $form ) {
if( ! $this->is_applicable_form( $form ) ) {
return;
}
$args = array(
'formId' => $form['id'],
'classRegex' => $this->class_regex
);
$script = 'new GWRounding( ' . json_encode( $args ) . ' );';
GFFormDisplay::add_init_script( $form['id'], 'gw_rounding', GFFormDisplay::ON_PAGE_RENDER, $script );
}
function enqueue_form_scripts( $form ) {
if( $this->is_applicable_form( $form ) ) {
wp_enqueue_script( 'gform_gravityforms' );
}
}
function override_submitted_value( $form ) {
foreach( $form['fields'] as $field ) {
if( $this->is_applicable_field( $field ) && ! is_a( $field, 'GF_Field_Calculation' ) ) {
$value = rgpost( "input_{$field['id']}" );
$is_currency = $field->get_input_type() == 'number' && $field->numberFormat == 'currency';
if( $is_currency ) {
$value = GFCommon::to_number( $value );
}
$value = $this->process_rounding_actions( $value, $this->get_rounding_actions( $field ) );
$_POST[ "input_{$field['id']}" ] = $value;
}
}
}
function override_submitted_calculation_value( $result, $formula, $field, $form, $entry ) {
if( $this->is_applicable_field( $field ) ) {
$result = $this->process_rounding_actions( $result, $this->get_rounding_actions( $field ) );
}
return $result;
}
function process_rounding_actions( $value, $actions ) {
foreach( $actions as $action ) {
$value = $this->round( $value, $action['action'], $action['action_value'] );
}
return $value;
}
function round( $value, $action, $action_value ) {
$value = floatval( $value );
$action_value = intval( $action_value );
switch( $action ) {
case 'min':
$min = $action_value;
if( $value < $min ) {
$value = $min;
}
break;
case 'max':
$max = $action_value;
if( $value > $max ) {
$value = $max;
}
break;
case 'up':
$interval = $action_value;
$base = ceil( $value / $interval );
$value = $base * $interval;
break;
case 'down':
$interval = $action_value;
$base = floor( $value / $interval );
$value = $base * $interval;
break;
default:
$interval = $action_value;
$base = round( $value / $interval );
$value = $base * $interval;
break;
}
return intval( $value );
}
// # HELPERS
function is_applicable_form( $form ) {
foreach( $form['fields'] as $field ) {
if( $this->is_applicable_field( $field ) ) {
return true;
}
}
return false;
}
function is_applicable_field( $field ) {
return preg_match( $this->get_class_regex(), rgar( $field, 'cssClass' ) ) == true;
}
function get_class_regex() {
return "/{$this->class_regex}/";
}
function get_rounding_actions( $field ) {
$actions = array();
preg_match_all( $this->get_class_regex(), rgar( $field, 'cssClass' ), $matches, PREG_SET_ORDER );
foreach( $matches as $match ) {
list( $full_match, $action, $action_value ) = array_pad( $match, 3, false );
if( $action_value === false ) {
$action_value = $action;
$action = 'round';
}
$action = array(
'action' => $action,
'action_value' => $action_value
);
$actions[] = $action;
}
return $actions;
}
}
function gw_rounding() {
return GW_Rounding::get_instance();
}
gw_rounding();
@donjajo
Copy link

donjajo commented Sep 17, 2018

Nice!
Working with currency input has some issues as converting dollar to int will return 0 so minimum is always returned. Made this as a workaround

function process_rounding_actions( $value, $actions ) {
        $currency = $value[ 0 ]; // get the currency symbol
        $value = preg_replace( '/([^\w\.])/', null, $value ); // Strip out currency symbol

        foreach( $actions as $action ) {
            $value = $this->round( $value, $action['action'], $action['action_value'] );
        }

        return sprintf( '%s %s', $currency, $value ); // Replace the currency symbol
    }

@spivurno
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment