PageRenderTime 24ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/shopaholic/lib/Nette/Forms/Renderers/InstantClientScript.php

http://github.com/jakubkulhan/shopaholic
PHP | 327 lines | 220 code | 70 blank | 37 comment | 58 complexity | a93a43c12813774d2d786007f125dca3 MD5 | raw file
Possible License(s): WTFPL
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette\Forms
  17. * @version $Id: InstantClientScript.php 201 2009-01-28 05:56:46Z david@grudl.com $
  18. */
  19. require_once dirname(__FILE__) . '/../../Object.php';
  20. /**
  21. * Instant validation JavaScript generator.
  22. *
  23. * @author David Grudl
  24. * @copyright Copyright (c) 2004, 2009 David Grudl
  25. * @package Nette\Forms
  26. */
  27. final class InstantClientScript extends Object
  28. {
  29. /** @var string JavaScript event handler name */
  30. public $validateFunction;
  31. /** @var string JavaScript event handler name */
  32. public $toggleFunction;
  33. /** @var string JavaScript code */
  34. public $doAlert = 'if (element) element.focus(); alert(message);';
  35. /** @var string JavaScript code */
  36. public $doToggle = 'if (element) element.style.display = visible ? "" : "none";';
  37. /** @var string */
  38. public $validateScript;
  39. /** @var string */
  40. public $toggleScript;
  41. /** @var bool */
  42. private $central;
  43. /** @var Form */
  44. private $form;
  45. public function __construct(Form $form)
  46. {
  47. $this->form = $form;
  48. $name = ucfirst($form->getName()); //ucfirst(strtr($form->getUniqueId(), Form::NAME_SEPARATOR, '_'));
  49. $this->validateFunction = 'validate' . $name;
  50. $this->toggleFunction = 'toggle' . $name;
  51. }
  52. public function enable()
  53. {
  54. $this->validateScript = '';
  55. $this->toggleScript = '';
  56. $this->central = TRUE;
  57. foreach ($this->form->getControls() as $control) {
  58. $script = $this->getValidateScript($control->getRules());
  59. if ($script) {
  60. $this->validateScript .= "do {\n\t$script} while(0);\n\n\t";
  61. }
  62. $this->toggleScript .= $this->getToggleScript($control->getRules());
  63. if ($control instanceof ISubmitterControl && $control->getValidationScope() !== TRUE) {
  64. $this->central = FALSE;
  65. }
  66. }
  67. if ($this->validateScript || $this->toggleScript) {
  68. if ($this->central) {
  69. $this->form->getElementPrototype()->onsubmit("return $this->validateFunction(this)", TRUE);
  70. } else {
  71. foreach ($this->form->getComponents(TRUE, 'Nette\Forms\ISubmitterControl') as $control) {
  72. if ($control->getValidationScope()) {
  73. $control->getControlPrototype()->onclick("return $this->validateFunction(this)", TRUE);
  74. }
  75. }
  76. }
  77. }
  78. }
  79. /**
  80. * Generates the client side validation script.
  81. * @return string
  82. */
  83. public function renderClientScript()
  84. {
  85. $s = '';
  86. if ($this->validateScript) {
  87. $s .= "function $this->validateFunction(sender) {\n\t"
  88. . "var element, message, res;\n\t"
  89. . $this->validateScript
  90. . "return true;\n"
  91. . "}\n\n";
  92. }
  93. if ($this->toggleScript) {
  94. $s .= "function $this->toggleFunction(sender) {\n\t"
  95. . "var element, visible, res;\n\t"
  96. . $this->toggleScript
  97. . "\n}\n\n"
  98. . "$this->toggleFunction(null);\n";
  99. }
  100. if ($s) {
  101. return "<script type=\"text/javascript\">\n"
  102. . "/* <![CDATA[ */\n"
  103. . $s
  104. . "/* ]]> */\n"
  105. . "</script>";
  106. }
  107. }
  108. private function getValidateScript(Rules $rules, $onlyCheck = FALSE)
  109. {
  110. $res = '';
  111. foreach ($rules as $rule) {
  112. if (!is_string($rule->operation)) continue;
  113. if (strcasecmp($rule->operation, 'Nette\Forms\InstantClientScript::javascript') === 0) {
  114. $res .= "$rule->arg\n\t";
  115. continue;
  116. }
  117. $script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
  118. if (!$script) continue;
  119. if (!empty($rule->message)) { // this is rule
  120. if ($onlyCheck) {
  121. $res .= "$script\n\tif (" . ($rule->isNegative ? '' : '!') . "res) { return false; }\n\t";
  122. } else {
  123. $res .= "$script\n\t"
  124. . "if (" . ($rule->isNegative ? '' : '!') . "res) { "
  125. . "message = " . json_encode((string) vsprintf($rule->control->translate($rule->message), (array) $rule->arg)) . "; "
  126. . $this->doAlert
  127. . " return false; }\n\t";
  128. }
  129. }
  130. if ($rule->type === Rule::CONDITION) { // this is condition
  131. $innerScript = $this->getValidateScript($rule->subRules, $onlyCheck);
  132. if ($innerScript) {
  133. $res .= "$script\n\tif (" . ($rule->isNegative ? '!' : '') . "res) {\n\t\t" . str_replace("\n\t", "\n\t\t", rtrim($innerScript)) . "\n\t}\n\t";
  134. if (!$onlyCheck && $rule->control instanceof ISubmitterControl) {
  135. $this->central = FALSE;
  136. }
  137. }
  138. }
  139. }
  140. return $res;
  141. }
  142. private function getToggleScript(Rules $rules, $cond = NULL)
  143. {
  144. $s = '';
  145. foreach ($rules->getToggles() as $id => $visible) {
  146. $s .= "visible = true; {$cond}element = document.getElementById('" . $id . "');\n\t"
  147. . ($visible ? '' : 'visible = !visible; ')
  148. . $this->doToggle
  149. . "\n\t";
  150. }
  151. foreach ($rules as $rule) {
  152. if ($rule->type === Rule::CONDITION && is_string($rule->operation)) {
  153. $script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
  154. if ($script) {
  155. $res = $this->getToggleScript($rule->subRules, $cond . "$script visible = visible && " . ($rule->isNegative ? '!' : '') . "res;\n\t");
  156. if ($res) {
  157. $el = $rule->control->getControlPrototype();
  158. if ($el->getName() === 'select') {
  159. $el->onchange("$this->toggleFunction(this)", TRUE);
  160. } else {
  161. $el->onclick("$this->toggleFunction(this)", TRUE);
  162. //$el->onkeyup("$this->toggleFunction(this)", TRUE);
  163. }
  164. $s .= $res;
  165. }
  166. }
  167. }
  168. }
  169. return $s;
  170. }
  171. private function getValueScript(IFormControl $control)
  172. {
  173. $tmp = "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\t";
  174. switch (TRUE) {
  175. case $control instanceof Checkbox:
  176. return $tmp . "var val = element.checked;\n\t";
  177. case $control instanceof RadioList:
  178. return "for (var val=null, i=0; i<" . count($control->getItems()) . "; i++) {\n\t\t"
  179. . "element = document.getElementById(" . json_encode($control->getHtmlId() . '-') . "+i);\n\t\t"
  180. . "if (element.checked) { val = element.value; break; }\n\t"
  181. . "}\n\t";
  182. default:
  183. return $tmp . "var val = element.value.replace(/^\\s+|\\s+\$/g, '');\n\t";
  184. }
  185. }
  186. private function getClientScript(IFormControl $control, $operation, $arg)
  187. {
  188. $operation = strtolower($operation);
  189. switch (TRUE) {
  190. case $control instanceof HiddenField || $control->isDisabled():
  191. return NULL;
  192. case $operation === ':filled' && $control instanceof RadioList:
  193. return $this->getValueScript($control) . "res = val !== null;";
  194. case $operation === ':submitted' && $control instanceof SubmitButton:
  195. return "element=null; res=sender && sender.name==" . json_encode($control->getHtmlName()) . ";";
  196. case $operation === ':equal' && $control instanceof MultiSelectBox:
  197. $tmp = array();
  198. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  199. $tmp[] = "element.options[i].value==" . json_encode((string) $item);
  200. }
  201. $first = $control->isFirstSkipped() ? 1 : 0;
  202. return "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\tres = false;\n\t"
  203. . "for (var i=$first;i<element.options.length;i++)\n\t\t"
  204. . "if (element.options[i].selected && (" . implode(' || ', $tmp) . ")) { res = true; break; }";
  205. case $operation === ':filled' && $control instanceof SelectBox:
  206. return "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\t"
  207. . "res = element.selectedIndex >= " . ($control->isFirstSkipped() ? 1 : 0) . ";";
  208. case $operation === ':filled' && $control instanceof TextInput:
  209. return $this->getValueScript($control) . "res = val!='' && val!=" . json_encode((string) $control->getEmptyValue()) . ";";
  210. case $operation === ':minlength' && $control instanceof TextBase:
  211. return $this->getValueScript($control) . "res = val.length>=" . (int) $arg . ";";
  212. case $operation === ':maxlength' && $control instanceof TextBase:
  213. return $this->getValueScript($control) . "res = val.length<=" . (int) $arg . ";";
  214. case $operation === ':length' && $control instanceof TextBase:
  215. if (!is_array($arg)) {
  216. $arg = array($arg, $arg);
  217. }
  218. return $this->getValueScript($control) . "res = " . ($arg[0] === NULL ? "true" : "val.length>=" . (int) $arg[0]) . " && "
  219. . ($arg[1] === NULL ? "true" : "val.length<=" . (int) $arg[1]) . ";";
  220. case $operation === ':email' && $control instanceof TextBase:
  221. return $this->getValueScript($control) . 'res = /^[^@]+@[^@]+\.[a-z]{2,6}$/i.test(val);';
  222. case $operation === ':url' && $control instanceof TextBase:
  223. return $this->getValueScript($control) . 'res = /^.+\.[a-z]{2,6}(\\/.*)?$/i.test(val);';
  224. case $operation === ':regexp' && $control instanceof TextBase:
  225. if (strncmp($arg, '/', 1)) {
  226. throw new InvalidStateException("Regular expression '$arg' must be JavaScript compatible.");
  227. }
  228. return $this->getValueScript($control) . "res = $arg.test(val);";
  229. case $operation === ':integer' && $control instanceof TextBase:
  230. return $this->getValueScript($control) . "res = /^-?[0-9]+$/.test(val);";
  231. case $operation === ':float' && $control instanceof TextBase:
  232. return $this->getValueScript($control) . "res = /^-?[0-9]*[.,]?[0-9]+$/.test(val);";
  233. case $operation === ':range' && $control instanceof TextBase:
  234. return $this->getValueScript($control) . "res = " . ($arg[0] === NULL ? "true" : "parseFloat(val)>=" . json_encode((float) $arg[0])) . " && "
  235. . ($arg[1] === NULL ? "true" : "parseFloat(val)<=" . json_encode((float) $arg[1])) . ";";
  236. case $operation === ':filled' && $control instanceof FormControl:
  237. return $this->getValueScript($control) . "res = val!='';";
  238. case $operation === ':valid' && $control instanceof FormControl:
  239. return $this->getValueScript($control) . "res = function(){\n\t" . $this->getValidateScript($control->getRules(), TRUE) . "return true; }();";
  240. case $operation === ':equal' && $control instanceof FormControl:
  241. if ($control instanceof Checkbox) $arg = (bool) $arg;
  242. $tmp = array();
  243. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  244. if ($item instanceof IFormControl) { // compare with another form control?
  245. $tmp[] = "val==function(){var element;" . $this->getValueScript($item). "return val;}()";
  246. } else {
  247. $tmp[] = "val==" . json_encode($item);
  248. }
  249. }
  250. return $this->getValueScript($control) . "res = (" . implode(' || ', $tmp) . ");";
  251. }
  252. }
  253. public static function javascript()
  254. {
  255. return TRUE;
  256. }
  257. }