Subversion Repositories web.creative

Rev

Blame | Last modification | View Log | Download

<?php namespace ProcessWire;

/**
 * Inputfield for floating point numbers
 * 
 * ProcessWire 3.x, Copyright 2020 by Ryan Cramer
 * https://processwire.com
 *
 * @property int $precision
 * @property string $inputType Input type to use, one of "text" or "number"
 * @property int|float $min
 * @property int|float $max
 * @property int|float|string $step
 * @property int $size
 * @property string $placeholder
 * @property int|float $initValue Initial/default value (when used as independent Inputfield)
 * @property int|float|string $defaultValue Initial/default value (when used with FieldtypeInteger)
 * 
 */

class InputfieldFloat extends InputfieldInteger {
  
  public static function getModuleInfo() {
    return array(
      'title' => __('Float', __FILE__), // Module Title
      'summary' => __('Floating point number with precision', __FILE__), // Module Summary
      'version' => 104,
      'permanent' => true, 
    );
  }

  /**
   * Construct
   * 
   */
  public function __construct() {
    $this->set('precision', 2); 
    parent::__construct();
  }

  /**
   * Module init
   * 
   */
  public function init() {
    parent::init();
    $this->attr('step', 'any'); // HTML5 attr required to support decimals with 'number' types
  }

  /**
   * Get configured precision setting, or if given a value, precision of the value
   * 
   * @param float|string|null $value
   * @return int|string Returns integer of precision or blank string if none defined
   * 
   */
  protected function getPrecision($value = null) {
    if($value !== null) return FieldtypeFloat::getPrecision($value);
    $precision = $this->precision;
    return $precision === null || $precision === '' ? '' : (int) $precision;
  }

  /**
   * Sanitize value 
   * 
   * @param float|string $value
   * @return float|string
   * 
   */
  protected function sanitizeValue($value) {
    if(!strlen("$value")) return '';
    if(!is_float($value) && !is_int($value)) {
      $value = $this->wire()->sanitizer->float($value, array('blankValue' => ''));
      if(!strlen("$value")) return '';
    }
    $precision = $this->precision;
    if($precision === null || $precision === '') {
      $precision = FieldtypeFloat::getPrecision($value);
    }
    return round((float) $value, $precision);
  }
  
  /**
   * Typecast value to float, override from InputfieldInteger
   *
   * @param string|int|float $value
   * @return int
   *
   */
  protected function typeValue($value) {
    return (float) $value;
  }

  /**
   * Override method from Inputfield to convert local specific decimals for input[type=number]
   * 
   * @param array $attributes
   * @return string
   * 
   */
  public function getAttributesString(array $attributes = null) {
    if($attributes && $attributes['type'] === 'number') { 
      $value = isset($attributes['value']) ? $attributes['value'] : null;
      if(is_float($value) && strlen("$value") && !ctype_digit(str_replace('.', '', $value))) {
        // float value is using a non "." as decimal point, needs conversion because
        // the HTML5 number input type requires "." as the decimal
        $attributes['value'] = $this->localeConvertValue($value); 
      }
    }
    return parent::getAttributesString($attributes);
  }

  /**
   * Convert floats with non "." decimal points to use "." decimal point according to locale
   * 
   * @param float|string $value
   * @return string|float Returns string representation of float when value was converted
   * 
   */
  protected function localeConvertValue($value) {
    if(!strlen("$value")) return $value; 
    if(ctype_digit(str_replace('.', '', $value))) return $value;
    $locale = localeconv();
    $decimal = $locale['decimal_point'];
    if($decimal === '.' || strpos($value, $decimal) === false) return $value;
    $parts = explode($decimal, $value, 2);
    $value = implode('.', $parts);
    return $value;
  }

  /**
   * Inputfield config
   * 
   * @return InputfieldWrapper
   * 
   */
  public function getConfigInputfields() {
    $inputfields = parent::getConfigInputfields();
    if($this->hasFieldtype === false) {
      /** @var InputfieldInteger $f */
      $f = $this->wire()->modules->get('InputfieldInteger');
      $f->attr('name', 'precision');
      $f->label = $this->_('Number of decimal digits to round to');
      $f->attr('value', $this->precision);
      $f->attr('size', 8);
      $inputfields->add($f);
    } else {
      // precision is configured with FieldtypeFloat
    }
    return $inputfields;
  }

}