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