diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 3c6e52dd4..164610b48 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -46,6 +46,7 @@ class Form extends Container implements Nette\Utils\IHtmlString EMAIL = ':email', URL = ':url', PATTERN = ':pattern', + PATTERN_ICASE = ':patternCaseInsensitive', INTEGER = ':integer', NUMERIC = ':numeric', FLOAT = ':float', diff --git a/src/Forms/Validator.php b/src/Forms/Validator.php index 042b2a0d2..3ebc2ae5f 100644 --- a/src/Forms/Validator.php +++ b/src/Forms/Validator.php @@ -246,12 +246,20 @@ public static function validateUrl(IControl $control): bool /** - * Matches control's value regular expression? + * Does the control's value match the regular expression? + * Case-sensitive to comply with the HTML5 pattern attribute behaviour */ - public static function validatePattern(IControl $control, string $pattern): bool + public static function validatePattern(IControl $control, string $pattern, bool $caseInsensitive = false): bool { $value = $control->getValue() instanceof Nette\Http\FileUpload ? $control->getValue()->getName() : $control->getValue(); - return (bool) Strings::match($value, "\x01^(?:$pattern)\\z\x01u"); + $modifiers = 'u' . ($caseInsensitive ? 'i' : ''); + return (bool) Strings::match($value, "\x01^(?:$pattern)\\z\x01" . $modifiers); + } + + + public static function validatePatternCaseInsensitive(IControl $control, string $pattern): bool + { + return self::validatePattern($control, $pattern, true); } diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js index e7bae0569..79825c334 100644 --- a/src/assets/netteForms.js +++ b/src/assets/netteForms.js @@ -448,16 +448,17 @@ } catch (e) {} // eslint-disable-line no-empty }, - pattern: function(elem, arg, val) { + pattern: function(elem, arg, val, value, caseInsensitive) { if (typeof arg !== 'string') { return null; } + var extraModifiers = (caseInsensitive ? 'i' : ''); try { try { - var regExp = new RegExp('^(?:' + arg + ')$', 'u'); + var regExp = new RegExp('^(?:' + arg + ')$', 'u' + extraModifiers); } catch (e) { - regExp = new RegExp('^(?:' + arg + ')$'); + regExp = new RegExp('^(?:' + arg + ')$', extraModifiers); } if (window.FileList && val instanceof FileList) { @@ -474,6 +475,10 @@ } catch (e) {} // eslint-disable-line no-empty }, + patternCaseInsensitive: function(elem, arg, val) { + return Nette.validators.pattern(elem, arg, val, null, true); + }, + numeric: function(elem, arg, val) { if (elem.type === 'number' && elem.validity.badInput) { return false; diff --git a/tests/Forms/Controls.TestBase.validators.phpt b/tests/Forms/Controls.TestBase.validators.phpt index 29d3de63b..3e6ac38fd 100644 --- a/tests/Forms/Controls.TestBase.validators.phpt +++ b/tests/Forms/Controls.TestBase.validators.phpt @@ -95,6 +95,14 @@ test(function () { Assert::false(Validator::validatePattern($control, '[0-9]+X')); }); +test(function () { + $control = new TextInput; + $control->value = '123x'; + Assert::false(Validator::validatePatternCaseInsensitive($control, '[0-9]')); + Assert::true(Validator::validatePatternCaseInsensitive($control, '[0-9]+x')); + Assert::true(Validator::validatePatternCaseInsensitive($control, '[0-9]+X')); +}); + test(function () { class MockUploadControl extends UploadControl diff --git a/tests/netteForms/spec/Nette.validateRuleSpec.js b/tests/netteForms/spec/Nette.validateRuleSpec.js index 938c286db..03fdace3f 100644 --- a/tests/netteForms/spec/Nette.validateRuleSpec.js +++ b/tests/netteForms/spec/Nette.validateRuleSpec.js @@ -45,6 +45,14 @@ describe('Nette.getValue & validateRule', function() { expect(Nette.validateRule(el, 'pattern', '\\d')).toBe(false); expect(Nette.validateRule(el, 'pattern', '\\w')).toBe(false); expect(Nette.validateRule(el, 'pattern', '\\w+')).toBe(true); + expect(Nette.validateRule(el, 'pattern', 'hello')).toBe(true); + expect(Nette.validateRule(el, 'pattern', 'HELLO')).toBe(false); + expect(Nette.validateRule(el, 'patternCaseInsensitive', '\\d+')).toBe(false); + expect(Nette.validateRule(el, 'patternCaseInsensitive', '\\d')).toBe(false); + expect(Nette.validateRule(el, 'patternCaseInsensitive', '\\w')).toBe(false); + expect(Nette.validateRule(el, 'patternCaseInsensitive', '\\w+')).toBe(true); + expect(Nette.validateRule(el, 'patternCaseInsensitive', 'hello')).toBe(true); + expect(Nette.validateRule(el, 'patternCaseInsensitive', 'HELLO')).toBe(true); expect(Nette.validateRule(el, 'integer')).toBe(false); expect(Nette.validateRule(el, 'float')).toBe(false);