MaskedInput.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\widgets;
  8. use yii\base\InvalidConfigException;
  9. use yii\helpers\Json;
  10. use yii\web\JsExpression;
  11. use yii\web\View;
  12. /**
  13. * MaskedInput generates a masked text input.
  14. *
  15. * MaskedInput is similar to [[Html::textInput()]] except that an input mask will be used to force users to enter
  16. * properly formatted data, such as phone numbers, social security numbers.
  17. *
  18. * To use MaskedInput, you must set the [[mask]] property. The following example
  19. * shows how to use MaskedInput to collect phone numbers:
  20. *
  21. * ```php
  22. * echo MaskedInput::widget([
  23. * 'name' => 'phone',
  24. * 'mask' => '999-999-9999',
  25. * ]);
  26. * ```
  27. *
  28. * You can also use this widget in an [[ActiveForm]] using the [[ActiveField::widget()|widget()]]
  29. * method, for example like this:
  30. *
  31. * ```php
  32. * <?= $form->field($model, 'from_date')->widget(\yii\widgets\MaskedInput::class, [
  33. * 'mask' => '999-999-9999',
  34. * ]) ?>
  35. * ```
  36. *
  37. * The masked text field is implemented based on the
  38. * [jQuery input masked plugin](https://github.com/RobinHerbots/Inputmask).
  39. *
  40. * @author Kartik Visweswaran <kartikv2@gmail.com>
  41. * @since 2.0
  42. */
  43. class MaskedInput extends InputWidget
  44. {
  45. /**
  46. * The name of the jQuery plugin to use for this widget.
  47. */
  48. const PLUGIN_NAME = 'inputmask';
  49. /**
  50. * @var string|array|JsExpression the input mask (e.g. '99/99/9999' for date input). The following characters
  51. * can be used in the mask and are predefined:
  52. *
  53. * - `a`: represents an alpha character (A-Z, a-z)
  54. * - `9`: represents a numeric character (0-9)
  55. * - `*`: represents an alphanumeric character (A-Z, a-z, 0-9)
  56. * - `[` and `]`: anything entered between the square brackets is considered optional user input. This is
  57. * based on the `optionalmarker` setting in [[clientOptions]].
  58. *
  59. * Additional definitions can be set through the [[definitions]] property.
  60. */
  61. public $mask;
  62. /**
  63. * @var array custom mask definitions to use. Should be configured as `maskSymbol => settings`, where
  64. *
  65. * - `maskSymbol` is a string, containing a character to identify your mask definition and
  66. * - `settings` is an array, consisting of the following entries:
  67. * - `validator`: string, a JS regular expression or a JS function.
  68. * - `cardinality`: int, specifies how many characters are represented and validated for the definition.
  69. * - `prevalidator`: array, validate the characters before the definition cardinality is reached.
  70. * - `definitionSymbol`: string, allows shifting values from other definitions, with this `definitionSymbol`.
  71. */
  72. public $definitions;
  73. /**
  74. * @var array custom aliases to use. Should be configured as `maskAlias => settings`, where
  75. *
  76. * - `maskAlias` is a string containing a text to identify your mask alias definition (e.g. 'phone') and
  77. * - `settings` is an array containing settings for the mask symbol, exactly similar to parameters as passed in [[clientOptions]].
  78. */
  79. public $aliases;
  80. /**
  81. * @var array the JQuery plugin options for the input mask plugin.
  82. * @see https://github.com/RobinHerbots/Inputmask
  83. */
  84. public $clientOptions = [];
  85. /**
  86. * @var array the HTML attributes for the input tag.
  87. * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  88. */
  89. public $options = ['class' => 'form-control'];
  90. /**
  91. * @var string the type of the input tag. Currently only 'text' and 'tel' are supported.
  92. * @see https://github.com/RobinHerbots/Inputmask
  93. * @since 2.0.6
  94. */
  95. public $type = 'text';
  96. /**
  97. * @var string the hashed variable to store the pluginOptions
  98. */
  99. protected $_hashVar;
  100. /**
  101. * Initializes the widget.
  102. *
  103. * @throws InvalidConfigException if the "mask" property is not set.
  104. */
  105. public function init()
  106. {
  107. parent::init();
  108. if (empty($this->mask) && empty($this->clientOptions['alias'])) {
  109. throw new InvalidConfigException("Either the 'mask' property or the 'clientOptions[\"alias\"]' property must be set.");
  110. }
  111. }
  112. /**
  113. * {@inheritdoc}
  114. */
  115. public function run()
  116. {
  117. $this->registerClientScript();
  118. echo $this->renderInputHtml($this->type);
  119. }
  120. /**
  121. * Generates a hashed variable to store the plugin `clientOptions`.
  122. *
  123. * Helps in reusing the variable for similar
  124. * options passed for other widgets on the same page. The following special data attribute will also be
  125. * added to the input field to allow accessing the client options via javascript:
  126. *
  127. * - 'data-plugin-inputmask' will store the hashed variable storing the plugin options.
  128. *
  129. * @param View $view the view instance
  130. * @author [Thiago Talma](https://github.com/thiagotalma)
  131. */
  132. protected function hashPluginOptions($view)
  133. {
  134. $encOptions = empty($this->clientOptions) ? '{}' : Json::htmlEncode($this->clientOptions);
  135. $this->_hashVar = self::PLUGIN_NAME . '_' . hash('crc32', $encOptions);
  136. $this->options['data-plugin-' . self::PLUGIN_NAME] = $this->_hashVar;
  137. $view->registerJs("var {$this->_hashVar} = {$encOptions};", View::POS_HEAD);
  138. }
  139. /**
  140. * Initializes client options.
  141. */
  142. protected function initClientOptions()
  143. {
  144. $options = $this->clientOptions;
  145. foreach ($options as $key => $value) {
  146. if (
  147. !$value instanceof JsExpression
  148. && in_array($key, [
  149. 'oncomplete', 'onincomplete', 'oncleared', 'onKeyUp', 'onKeyDown', 'onBeforeMask',
  150. 'onBeforePaste', 'onUnMask', 'isComplete', 'determineActiveMasksetIndex',
  151. ], true)
  152. ) {
  153. $options[$key] = new JsExpression($value);
  154. }
  155. }
  156. $this->clientOptions = $options;
  157. }
  158. /**
  159. * Registers the needed client script and options.
  160. */
  161. public function registerClientScript()
  162. {
  163. $js = '';
  164. $view = $this->getView();
  165. $this->initClientOptions();
  166. if (!empty($this->mask)) {
  167. $this->clientOptions['mask'] = $this->mask;
  168. }
  169. $this->hashPluginOptions($view);
  170. if (is_array($this->definitions) && !empty($this->definitions)) {
  171. $js .= ucfirst(self::PLUGIN_NAME) . '.extendDefinitions(' . Json::htmlEncode($this->definitions) . ');';
  172. }
  173. if (is_array($this->aliases) && !empty($this->aliases)) {
  174. $js .= ucfirst(self::PLUGIN_NAME) . '.extendAliases(' . Json::htmlEncode($this->aliases) . ');';
  175. }
  176. $id = $this->options['id'];
  177. $js .= 'jQuery("#' . $id . '").' . self::PLUGIN_NAME . '(' . $this->_hashVar . ');';
  178. MaskedInputAsset::register($view);
  179. $view->registerJs($js);
  180. }
  181. }