Source for file InstantClientScript.php

Documentation is available at InstantClientScript.php

  1. 1: <?php
  2. 2:  
  3. 3: /**
  4. 4:  * Nette Framework
  5. 5:  *
  6. 6:  * @copyright  Copyright (c) 2004, 2010 David Grudl
  7. 7:  * @license    http://nettephp.com/license  Nette license
  8. 8:  * @link       http://nettephp.com
  9. 9:  * @category   Nette
  10. 10:  * @package    Nette\Forms
  11. 11:  */
  12. 12:  
  13. 13:  
  14. 14:  
  15. 15: /**
  16. 16:  * Instant validation JavaScript generator.
  17. 17:  *
  18. 18:  * @copyright  Copyright (c) 2004, 2010 David Grudl
  19. 19:  * @package    Nette\Forms
  20. 20:  */
  21. 21: final class InstantClientScript extends Object
  22. 22: {
  23. 23:     /** @var array */
  24. 24:     private $validateScripts;
  25. 25:  
  26. 26:     /** @var string */
  27. 27:     private $toggleScript;
  28. 28:  
  29. 29:     /** @var bool */
  30. 30:     private $central;
  31. 31:  
  32. 32:     /** @var Form */
  33. 33:     private $form;
  34. 34:  
  35. 35:  
  36. 36:  
  37. 37:     public function __construct(Form $form)
  38. 38:     {
  39. 39:         $this->form $form;
  40. 40:     }
  41. 41:  
  42. 42:  
  43. 43:  
  44. 44:     public function enable()
  45. 45:     {
  46. 46:         $el $this->form->getElementPrototype();
  47. 47:         if (!$el->name{
  48. 48:             $el->name 'frm-' ($this->form instanceof AppForm $this->form->lookupPath('Nette\Application\Presenter'TRUE$this->form->getName());
  49. 49:         }
  50. 50:         $this->validateScripts array();
  51. 51:         $this->toggleScript '';
  52. 52:         $this->central TRUE;
  53. 53:  
  54. 54:         foreach ($this->form->getControls(as $control{
  55. 55:             $script $this->getValidateScript($control->getRules());
  56. 56:             if ($script{
  57. 57:                 $this->validateScripts[$control->getHtmlName()$script;
  58. 58:             }
  59. 59:             $this->toggleScript .= $this->getToggleScript($control->getRules());
  60. 60:  
  61. 61:             if ($control instanceof ISubmitterControl && $control->getValidationScope(!== TRUE{
  62. 62:                 $this->central FALSE;
  63. 63:             }
  64. 64:         }
  65. 65:  
  66. 66:         if ($this->validateScripts || $this->toggleScript{
  67. 67:             if ($this->central{
  68. 68:                 $this->form->getElementPrototype()->onsubmit("return nette.validateForm(this)"TRUE);
  69. 69:  
  70. 70:             else {
  71. 71:                 foreach ($this->form->getComponents(TRUE'Nette\Forms\ISubmitterControl'as $control{
  72. 72:                     if ($control->getValidationScope()) {
  73. 73:                         $control->getControlPrototype()->onclick("return nette.validateForm(this)"TRUE);
  74. 74:                     }
  75. 75:                 }
  76. 76:             }
  77. 77:         }
  78. 78:     }
  79. 79:  
  80. 80:  
  81. 81:  
  82. 82:     /**
  83. 83:      * Generates the client side validation script.
  84. 84:      * @return string 
  85. 85:      */
  86. 86:     public function renderClientScript()
  87. 87:     {
  88. 88:         if (!$this->validateScripts && !$this->toggleScript{
  89. 89:             return;
  90. 90:         }
  91. 91:  
  92. 92:         $formName json_encode((string) $this->form->getElementPrototype()->name);
  93. 93:         ob_start();
  94. 94:         include dirname(__FILE__'/InstantClientScript.phtml';
  95. 95:         return ob_get_clean();
  96. 96:     }
  97. 97:  
  98. 98:  
  99. 99:  
  100. 100:     private function getValidateScript(Rules $rules)
  101. 101:     {
  102. 102:         $res '';
  103. 103:         foreach ($rules as $rule{
  104. 104:             if (!is_string($rule->operation)) continue;
  105. 105:  
  106. 106:             if (strcasecmp($rule->operation'Nette\Forms\InstantClientScript::javascript'=== 0{
  107. 107:                 $res .= "$rule->arg\n";
  108. 108:                 continue;
  109. 109:             }
  110. 110:  
  111. 111:             $script $this->getClientScript($rule->control$rule->operation$rule->arg);
  112. 112:             if (!$scriptcontinue;
  113. 113:  
  114. 114:             if (!empty($rule->message)) // this is rule
  115. 115:                 $res .= "$script\n"
  116. 116:                     . "if (" ($rule->isNegative '' '!'"res) "
  117. 117:                     . "return " json_encode((string) vsprintf($rule->control->translate($rule->messageis_int($rule->arg$rule->arg NULL)(array) $rule->arg)) ";\n";
  118. 118:             }
  119. 119:  
  120. 120:             if ($rule->type === Rule::CONDITION// this is condition
  121. 121:                 $innerScript $this->getValidateScript($rule->subRules);
  122. 122:                 if ($innerScript{
  123. 123:                     $res .= "$script\nif (($rule->isNegative '!' ''"res) {\n" String::indent($innerScript"}\n";
  124. 124:                     if ($rule->control instanceof ISubmitterControl{
  125. 125:                         $this->central FALSE;
  126. 126:                     }
  127. 127:                 }
  128. 128:             }
  129. 129:         }
  130. 130:         return $res;
  131. 131:     }
  132. 132:  
  133. 133:  
  134. 134:  
  135. 135:     private function getToggleScript(Rules $rules$cond NULL)
  136. 136:     {
  137. 137:         $s '';
  138. 138:         foreach ($rules->getToggles(as $id => $visible{
  139. 139:             $s .= "visible = true; {$cond}\n"
  140. 140:                 . "nette.toggle(" json_encode((string) $id", " ($visible '' '!'"visible);\n";
  141. 141:         }
  142. 142:         $formName json_encode((string) $this->form->getElementPrototype()->name);
  143. 143:         foreach ($rules as $rule{
  144. 144:             if ($rule->type === Rule::CONDITION && is_string($rule->operation)) {
  145. 145:                 $script $this->getClientScript($rule->control$rule->operation$rule->arg);
  146. 146:                 if ($script{
  147. 147:                     $res $this->getToggleScript($rule->subRules$cond "$script visible = visible && ($rule->isNegative '!' ''"res;\n");
  148. 148:                     if ($res{
  149. 149:                         $el $rule->control->getControlPrototype();
  150. 150:                         if ($el->getName(=== 'select'{
  151. 151:                             $el->onchange("nette.forms[$formName].toggle(this)"TRUE);
  152. 152:                         else {
  153. 153:                             $el->onclick("nette.forms[$formName].toggle(this)"TRUE);
  154. 154:                             //$el->onkeyup("nette.forms[$formName].toggle(this)", TRUE);
  155. 155:                         }
  156. 156:                         $s .= $res;
  157. 157:                     }
  158. 158:                 }
  159. 159:             }
  160. 160:         }
  161. 161:         return $s;
  162. 162:     }
  163. 163:  
  164. 164:  
  165. 165:  
  166. 166:     private function getClientScript(IFormControl $control$operation$arg)
  167. 167:     {
  168. 168:         $operation strtolower($operation);
  169. 169:         $elem 'form[' json_encode($control->getHtmlName()) ']';
  170. 170:  
  171. 171:         switch (TRUE{
  172. 172:         case $control instanceof HiddenField || $control->isDisabled():
  173. 173:             return NULL;
  174. 174:  
  175. 175:         case $operation === ':filled' && $control instanceof RadioList:
  176. 176:             return "res = nette.getValue($elem) !== null;";
  177. 177:  
  178. 178:         case $operation === ':submitted' && $control instanceof SubmitButton:
  179. 179:             return "res=sender && sender.name==" json_encode($control->getHtmlName()) ";";
  180. 180:  
  181. 181:         case $operation === ':equal' && $control instanceof MultiSelectBox:
  182. 182:             $tmp array();
  183. 183:             foreach ((is_array($arg$arg array($arg)) as $item{
  184. 184:                 $tmp["options[i].value==" json_encode((string) $item);
  185. 185:             }
  186. 186:             $first $control->isFirstSkipped(0;
  187. 187:             return "var options = $elem.options; res = false;\n"
  188. 188:                 . "for (var i=$first, len=options.length; i<len; i++)\n\t"
  189. 189:                 . "if (options[i].selected && (" implode(' || '$tmp")) { res = true; break; }";
  190. 190:  
  191. 191:         case $operation === ':filled' && $control instanceof SelectBox:
  192. 192:             return "res = $elem.selectedIndex >= ($control->isFirstSkipped(0";";
  193. 193:  
  194. 194:         case $operation === ':filled' && $control instanceof TextBase:
  195. 195:             return "var val = nette.getValue($elem); res = val!='' && val!=json_encode((string) $control->getEmptyValue()) ";";
  196. 196:  
  197. 197:         case $operation === ':minlength' && $control instanceof TextBase:
  198. 198:             return "res = nette.getValue($elem).length>=. (int) $arg ";";
  199. 199:  
  200. 200:         case $operation === ':maxlength' && $control instanceof TextBase:
  201. 201:             return "res = nette.getValue($elem).length<=. (int) $arg ";";
  202. 202:  
  203. 203:         case $operation === ':length' && $control instanceof TextBase:
  204. 204:             if (!is_array($arg)) {
  205. 205:                 $arg array($arg$arg);
  206. 206:             }
  207. 207:             return "var val = nette.getValue($elem); res = ($arg[0=== NULL "true" "val.length>=" . (int) $arg[0]" && "
  208. 208:                 . ($arg[1=== NULL "true" "val.length<=" . (int) $arg[1]";";
  209. 209:  
  210. 210:         case $operation === ':email' && $control instanceof TextBase:
  211. 211:             return 'res = /^[^@\s]+@[^@\s]+\.[a-z]{2,10}$/i.test(nette.getValue('.$elem.'));';
  212. 212:  
  213. 213:         case $operation === ':url' && $control instanceof TextBase:
  214. 214:             return 'res = /^.+\.[a-z]{2,6}(\\/.*)?$/i.test(nette.getValue('.$elem.'));';
  215. 215:  
  216. 216:         case $operation === ':regexp' && $control instanceof TextBase:
  217. 217:             if (!preg_match('#^(/.*/)([imu]*)$#'$arg$matches)) {
  218. 218:                 return NULL// regular expression must be JavaScript compatible
  219. 219:             }
  220. 220:             $arg $matches[1str_replace('u'''$matches[2]);
  221. 221:             return "res = $arg.test(nette.getValue($elem));";
  222. 222:  
  223. 223:         case $operation === ':integer' && $control instanceof TextBase:
  224. 224:             return "res = /^-?[0-9]+$/.test(nette.getValue($elem));";
  225. 225:  
  226. 226:         case $operation === ':float' && $control instanceof TextBase:
  227. 227:             return "res = /^-?[0-9]*[.,]?[0-9]+$/.test(nette.getValue($elem));";
  228. 228:  
  229. 229:         case $operation === ':range' && $control instanceof TextBase:
  230. 230:             return "var val = nette.getValue($elem); res = ($arg[0=== NULL "true" "parseFloat(val)>=" json_encode((float) $arg[0])) " && "
  231. 231:                 . ($arg[1=== NULL "true" "parseFloat(val)<=" json_encode((float) $arg[1])) ";";
  232. 232:  
  233. 233:         case $operation === ':filled' && $control instanceof FormControl:
  234. 234:             return "res = nette.getValue($elem) != '';";
  235. 235:  
  236. 236:         case $operation === ':valid' && $control instanceof FormControl:
  237. 237:             return "res = !this[" json_encode($control->getHtmlName()) "](sender);";
  238. 238:  
  239. 239:         case $operation === ':equal' && $control instanceof FormControl:
  240. 240:             if ($control instanceof Checkbox$arg = (bool) $arg;
  241. 241:             $tmp array();
  242. 242:             foreach ((is_array($arg$arg array($arg)) as $item{
  243. 243:                 if ($item instanceof IFormControl// compare with another form control?
  244. 244:                     $tmp["val==nette.getValue(form[" json_encode($item->getHtmlName()) "])";
  245. 245:                 else {
  246. 246:                     $tmp["val==" json_encode($item);
  247. 247:                 }
  248. 248:             }
  249. 249:             return "var val = nette.getValue($elem); res = (implode(' || '$tmp");";
  250. 250:         }
  251. 251:     }
  252. 252:  
  253. 253:  
  254. 254:  
  255. 255:     public static function javascript()
  256. 256:     {
  257. 257:         return TRUE;
  258. 258:     }
  259. 259: