PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Forms/Renderers/InstantClientScript.php

https://code.google.com/
PHP | 258 lines | 178 code | 58 blank | 22 comment | 54 complexity | b957f3ee60334a39e85d1cd36a30fb51 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework.
  4. *
  5. * Copyright (c) 2004, 2010 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license", and/or
  8. * GPL license. For more information please see http://nette.org
  9. */
  10. namespace Nette\Forms;
  11. use Nette;
  12. /**
  13. * Instant validation JavaScript generator.
  14. *
  15. * @author David Grudl
  16. */
  17. final class InstantClientScript extends Nette\Object
  18. {
  19. /** @var array */
  20. private $validateScripts;
  21. /** @var string */
  22. private $toggleScript;
  23. /** @var bool */
  24. private $central;
  25. /** @var Form */
  26. private $form;
  27. public function __construct(Form $form)
  28. {
  29. $this->form = $form;
  30. }
  31. public function enable()
  32. {
  33. $this->validateScripts = array();
  34. $this->toggleScript = '';
  35. $this->central = TRUE;
  36. foreach ($this->form->getControls() as $control) {
  37. $script = $this->getValidateScript($control->getRules());
  38. if ($script) {
  39. $this->validateScripts[$control->getHtmlName()] = $script;
  40. }
  41. $this->toggleScript .= $this->getToggleScript($control->getRules());
  42. if ($control instanceof ISubmitterControl && $control->getValidationScope() !== TRUE) {
  43. $this->central = FALSE;
  44. }
  45. }
  46. if ($this->validateScripts || $this->toggleScript) {
  47. if ($this->central) {
  48. $this->form->getElementPrototype()->onsubmit("return nette.validateForm(this)", TRUE);
  49. } else {
  50. foreach ($this->form->getComponents(TRUE, 'Nette\Forms\ISubmitterControl') as $control) {
  51. if ($control->getValidationScope()) {
  52. $control->getControlPrototype()->onclick("return nette.validateForm(this)", TRUE);
  53. }
  54. }
  55. }
  56. }
  57. }
  58. /**
  59. * Generates the client side validation script.
  60. * @return string
  61. */
  62. public function renderClientScript()
  63. {
  64. if (!$this->validateScripts && !$this->toggleScript) {
  65. return;
  66. }
  67. $formName = json_encode((string) $this->form->getElementPrototype()->id);
  68. ob_start();
  69. require __DIR__ . '/InstantClientScript.phtml';
  70. return ob_get_clean();
  71. }
  72. private function getValidateScript(Rules $rules)
  73. {
  74. $res = '';
  75. foreach ($rules as $rule) {
  76. if (!is_string($rule->operation)) continue;
  77. if (strcasecmp($rule->operation, 'Nette\\Forms\\InstantClientScript::javascript') === 0) {
  78. $res .= "$rule->arg\n";
  79. continue;
  80. }
  81. $script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
  82. if (!$script) continue;
  83. if ($rule->type === Rule::VALIDATOR && !empty($rule->message)) {
  84. $message = Rules::formatMessage($rule, FALSE);
  85. $res .= "$script\n"
  86. . "if (" . ($rule->isNegative ? '' : '!') . "res) "
  87. . "return " . json_encode((string) $message) . (strpos($message, '%value') === FALSE ? '' : ".replace('%value', val);\n") . ";\n";
  88. } elseif ($rule->type === Rule::CONDITION) {
  89. $innerScript = $this->getValidateScript($rule->subRules);
  90. if ($innerScript) {
  91. $res .= "$script\nif (" . ($rule->isNegative ? '!' : '') . "res) {\n" . Nette\String::indent($innerScript) . "}\n";
  92. if ($rule->control instanceof ISubmitterControl) {
  93. $this->central = FALSE;
  94. }
  95. }
  96. }
  97. }
  98. return $res;
  99. }
  100. private function getToggleScript(Rules $rules, $cond = NULL)
  101. {
  102. $s = '';
  103. foreach ($rules->getToggles() as $id => $visible) {
  104. $s .= "visible = true; {$cond}\n"
  105. . "nette.toggle(" . json_encode((string) $id) . ", " . ($visible ? '' : '!') . "visible);\n";
  106. }
  107. $formName = json_encode((string) $this->form->getElementPrototype()->id);
  108. foreach ($rules as $rule) {
  109. if ($rule->type === Rule::CONDITION && is_string($rule->operation)) {
  110. $script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
  111. if ($script) {
  112. $res = $this->getToggleScript($rule->subRules, $cond . "$script visible = visible && " . ($rule->isNegative ? '!' : '') . "res;\n");
  113. if ($res) {
  114. $el = $rule->control->getControlPrototype();
  115. if ($el->getName() === 'select') {
  116. $el->onchange("nette.forms[$formName].toggle(this)", TRUE);
  117. } else {
  118. $el->onclick("nette.forms[$formName].toggle(this)", TRUE);
  119. //$el->onkeyup("nette.forms[$formName].toggle(this)", TRUE);
  120. }
  121. $s .= $res;
  122. }
  123. }
  124. }
  125. }
  126. return $s;
  127. }
  128. private function getClientScript(IFormControl $control, $operation, $arg)
  129. {
  130. $operation = strtolower($operation);
  131. $elem = 'form[' . json_encode($control->getHtmlName()) . ']';
  132. switch (TRUE) {
  133. case $control instanceof HiddenField || $control->isDisabled():
  134. return NULL;
  135. case $operation === ':filled' && $control instanceof RadioList:
  136. return "res = (val = nette.getValue($elem)) !== null;";
  137. case $operation === ':submitted' && $control instanceof SubmitButton:
  138. return "res = sender && sender.name==" . json_encode($control->getHtmlName()) . ";";
  139. case $operation === ':equal' && $control instanceof MultiSelectBox:
  140. $tmp = array();
  141. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  142. $tmp[] = "options[i].value==" . json_encode((string) $item);
  143. }
  144. $first = $control->isFirstSkipped() ? 1 : 0;
  145. return "var options = $elem.options; res = false;\n"
  146. . "for (var i=$first, len=options.length; i<len; i++)\n\t"
  147. . "if (options[i].selected && (" . implode(' || ', $tmp) . ")) { res = true; break; }";
  148. case $operation === ':filled' && $control instanceof SelectBox:
  149. return "res = $elem.selectedIndex >= " . ($control->isFirstSkipped() ? 1 : 0) . ";";
  150. case $operation === ':filled' && $control instanceof TextBase:
  151. return "val = nette.getValue($elem); res = val!='' && val!=" . json_encode((string) $control->getEmptyValue()) . ";";
  152. case $operation === ':minlength' && $control instanceof TextBase:
  153. return "res = (val = nette.getValue($elem)).length>=" . (int) $arg . ";";
  154. case $operation === ':maxlength' && $control instanceof TextBase:
  155. return "res = (val = nette.getValue($elem)).length<=" . (int) $arg . ";";
  156. case $operation === ':length' && $control instanceof TextBase:
  157. if (!is_array($arg)) {
  158. $arg = array($arg, $arg);
  159. }
  160. return "val = nette.getValue($elem); res = " . ($arg[0] === NULL ? "true" : "val.length>=" . (int) $arg[0]) . " && "
  161. . ($arg[1] === NULL ? "true" : "val.length<=" . (int) $arg[1]) . ";";
  162. case $operation === ':email' && $control instanceof TextBase:
  163. return 'res = /^[^@\s]+@[^@\s]+\.[a-z]{2,10}$/i.test(val = nette.getValue('.$elem.'));';
  164. case $operation === ':url' && $control instanceof TextBase:
  165. return 'res = /^.+\.[a-z]{2,6}(\\/.*)?$/i.test(val = nette.getValue('.$elem.'));';
  166. case $operation === ':regexp' && $control instanceof TextBase:
  167. if (!preg_match('#^(/.*/)([imu]*)$#', $arg, $matches)) {
  168. return NULL; // regular expression must be JavaScript compatible
  169. }
  170. $arg = $matches[1] . str_replace('u', '', $matches[2]);
  171. return "res = $arg.test(val = nette.getValue($elem));";
  172. case $operation === ':integer' && $control instanceof TextBase:
  173. return "res = /^-?[0-9]+$/.test(val = nette.getValue($elem));";
  174. case $operation === ':float' && $control instanceof TextBase:
  175. return "res = /^-?[0-9]*[.,]?[0-9]+$/.test(val = nette.getValue($elem));";
  176. case $operation === ':range' && $control instanceof TextBase:
  177. return "val = nette.getValue($elem); res = " . ($arg[0] === NULL ? "true" : "parseFloat(val)>=" . json_encode((float) $arg[0])) . " && "
  178. . ($arg[1] === NULL ? "true" : "parseFloat(val)<=" . json_encode((float) $arg[1])) . ";";
  179. case $operation === ':filled' && $control instanceof FormControl:
  180. return "res = (val = nette.getValue($elem)) != '';";
  181. case $operation === ':valid' && $control instanceof FormControl:
  182. return "res = !this[" . json_encode($control->getHtmlName()) . "](sender);";
  183. case $operation === ':equal' && $control instanceof FormControl:
  184. if ($control instanceof Checkbox) $arg = (bool) $arg;
  185. $tmp = array();
  186. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  187. if ($item instanceof IFormControl) { // compare with another form control?
  188. $tmp[] = "val==nette.getValue(form[" . json_encode($item->getHtmlName()) . "])";
  189. } else {
  190. $tmp[] = "val==" . json_encode($item);
  191. }
  192. }
  193. return "val = nette.getValue($elem); res = (" . implode(' || ', $tmp) . ");";
  194. }
  195. }
  196. public static function javascript()
  197. {
  198. return TRUE;
  199. }
  200. }