You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
7.0KB

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