From 9df9e7fbfa8c30acf16d894b867539d70ec3f734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kliment?= Date: Wed, 25 Jul 2018 12:01:29 +0200 Subject: [PATCH 1/7] netteForms.js: support checking file name via pattern rule using HTML5 File API (#186) * this is a complement to #175 so validating file names via pattern works on both client and server --- src/assets/netteForms.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js index 95255f3df..fbda0162d 100644 --- a/src/assets/netteForms.js +++ b/src/assets/netteForms.js @@ -456,8 +456,24 @@ }, pattern: function(elem, arg, val) { + if (typeof arg !== 'string') { + return null; + } + try { - return typeof arg === 'string' ? (new RegExp('^(?:' + arg + ')$')).test(val) : null; + var regExp = new RegExp('^(?:' + arg + ')$'); + + if (window.FileList && val instanceof FileList) { + for (var i = 0; i < val.length; i++) { + if (!regExp.test(val[i].name)) { + return false; + } + } + + return true; + } + + return regExp.test(val); } catch (e) {} // eslint-disable-line no-empty }, From 348f7d0e6b40190c9f300ae3b6726f216f2185a3 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 25 Jul 2018 17:53:17 +0200 Subject: [PATCH 2/7] netteForms: uses unicode RegExp if is supported --- src/assets/netteForms.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js index fbda0162d..68eecc692 100644 --- a/src/assets/netteForms.js +++ b/src/assets/netteForms.js @@ -461,7 +461,11 @@ } try { - var regExp = new RegExp('^(?:' + arg + ')$'); + try { + var regExp = new RegExp('^(?:' + arg + ')$', 'u'); + } catch (e) { + regExp = new RegExp('^(?:' + arg + ')$'); + } if (window.FileList && val instanceof FileList) { for (var i = 0; i < val.length; i++) { From e3d2294f879004783478778065776b5fa71329c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kliment?= Date: Fri, 27 Jul 2018 09:35:57 +0200 Subject: [PATCH 3/7] add Form::PATTERN_ICASE as a case-insensitive variant of Form::PATTERN; fixes #185 (#187) --- src/Forms/Form.php | 1 + src/Forms/Validator.php | 13 ++++++++++--- src/assets/netteForms.js | 10 +++++++--- tests/Forms/Controls.TestBase.validators.phpt | 8 ++++++++ tests/netteForms/spec/Nette.validateRuleSpec.js | 8 ++++++++ 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 2ba2cb3ee..7a6078a8c 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -44,6 +44,7 @@ class Form extends Container implements Nette\Utils\IHtmlString EMAIL = ':email', URL = ':url', PATTERN = ':pattern', + PATTERN_ICASE = ':patternCaseInsensitive', INTEGER = ':integer', NUMERIC = ':integer', FLOAT = ':float', diff --git a/src/Forms/Validator.php b/src/Forms/Validator.php index 3a6b2ee5f..5b4272174 100644 --- a/src/Forms/Validator.php +++ b/src/Forms/Validator.php @@ -270,14 +270,21 @@ public static function validateUrl(IControl $control) /** - * 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 * @param string * @return bool */ - public static function validatePattern(IControl $control, $pattern) + public static function validatePattern(IControl $control, $pattern, $caseInsensitive = false) { $value = $control->getValue() instanceof Nette\Http\FileUpload ? $control->getValue()->getName() : $control->getValue(); - return (bool) Strings::match($value, "\x01^(?:$pattern)\\z\x01u"); + return (bool) Strings::match($value, "\x01^(?:$pattern)\\z\x01u" . ($caseInsensitive ? 'i' : '')); + } + + + public static function validatePatternCaseInsensitive(IControl $control, $pattern) + { + return self::validatePattern($control, $pattern, true); } diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js index 68eecc692..383e86730 100644 --- a/src/assets/netteForms.js +++ b/src/assets/netteForms.js @@ -455,16 +455,16 @@ } 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; } try { try { - var regExp = new RegExp('^(?:' + arg + ')$', 'u'); + var regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'ui' : 'u'); } catch (e) { - regExp = new RegExp('^(?:' + arg + ')$'); + regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : ''); } if (window.FileList && val instanceof FileList) { @@ -481,6 +481,10 @@ } catch (e) {} // eslint-disable-line no-empty }, + patternCaseInsensitive: function(elem, arg, val) { + return Nette.validators.pattern(elem, arg, val, null, true); + }, + integer: 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 34a9156a1..6aad90fb5 100644 --- a/tests/Forms/Controls.TestBase.validators.phpt +++ b/tests/Forms/Controls.TestBase.validators.phpt @@ -93,6 +93,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); From 0fa9e5ec8d2966eaa6e52eb146faf85e4459f1f5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 7 Aug 2018 15:36:02 +0200 Subject: [PATCH 4/7] BaseControl: added ability to multiple forms with different HTML ID [Closes #188] Solution: set Nette\Forms\Controls\BaseControl::$idMask = 'frm-%2$s-%1$s'; --- src/Forms/Controls/BaseControl.php | 3 +- tests/Forms/Forms.idMask.phpt | 55 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/Forms/Forms.idMask.phpt diff --git a/src/Forms/Controls/BaseControl.php b/src/Forms/Controls/BaseControl.php index ace5d7eeb..4cbb4e08d 100644 --- a/src/Forms/Controls/BaseControl.php +++ b/src/Forms/Controls/BaseControl.php @@ -334,7 +334,8 @@ public function setHtmlId($id) public function getHtmlId() { if (!isset($this->control->id)) { - $this->control->id = sprintf(self::$idMask, $this->lookupPath()); + $form = $this->getForm(false); + $this->control->id = sprintf(self::$idMask, $this->lookupPath(), $form ? $form->getName() : ''); } return $this->control->id; } diff --git a/tests/Forms/Forms.idMask.phpt b/tests/Forms/Forms.idMask.phpt new file mode 100644 index 000000000..ed7ddbbbe --- /dev/null +++ b/tests/Forms/Forms.idMask.phpt @@ -0,0 +1,55 @@ +getHtmlId(); +}, Nette\InvalidStateException::class, "Component '' is not attached to ''."); + + +test(function () { + $container = new Nette\Forms\Container; + $container->setParent(null, 'second'); + $input = $container->addText('name'); + Assert::same('frm-name', $input->getHtmlId()); +}); + + +test(function () { + $form = new Form; + $container = $form->addContainer('second'); + $input = $container->addText('name'); + Assert::same('frm-second-name', $input->getHtmlId()); +}); + + +test(function () { + $form = new Form; + $input = $form->addText('name'); + Assert::same('frm-name', $input->getHtmlId()); +}); + + +test(function () { + Nette\Forms\Controls\BaseControl::$idMask = 'frm-%s-%s'; + + $form = new Form; + $input = $form->addText('name'); + Assert::same('frm-name-', $input->getHtmlId()); +}); + + +test(function () { + Nette\Forms\Controls\BaseControl::$idMask = 'frm-%2$s-%1$s'; + + $form = new Form('signForm'); + $input = $form->addText('name'); + Assert::same('frm-signForm-name', $input->getHtmlId()); +}); From f104e0952abc8b9b83764abc5eb45f05b1c799dc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 13 Sep 2018 01:36:09 +0200 Subject: [PATCH 5/7] travis: uses NCS 2 --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82e211951..0902d63d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,9 @@ jobs: php: 7.1 install: # Install Nette Code Checker - - travis_retry composer create-project nette/code-checker temp/code-checker ~2 --no-progress + - travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress # Install Nette Coding Standard - - travis_retry composer create-project nette/coding-standard temp/coding-standard --no-progress + - travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress # Install new Node.js - . $HOME/.nvm/nvm.sh - nvm install stable @@ -40,8 +40,8 @@ jobs: # Install Grunt and Eslint - npm install -g grunt-cli; cd tests/netteForms; npm install; cd ../.. script: - - php temp/code-checker/src/code-checker.php --short-arrays - - php temp/coding-standard/ecs check src tests examples --config temp/coding-standard/coding-standard-php56.neon + - php temp/code-checker/code-checker + - php temp/coding-standard/ecs check src tests examples --config temp/coding-standard/coding-standard-php56.yml - grunt --gruntfile=tests/netteForms/Gruntfile.js test - tests/netteForms/node_modules/.bin/eslint src/assets/netteForms.js --config tests/.eslintrc.js From ace4611f735854356e097cdbd4800ecc37293c29 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 16 Sep 2018 20:31:55 +0200 Subject: [PATCH 6/7] package.json: updated version [Closes #195] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7aaa1969..6307002cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nette-forms", - "version": "2.4.2", + "version": "2.4.9", "description": "Client side script for Nette Forms Component", "keywords": [ "nette", From 787a924d09f507c44ead60f3822c31d61d909b65 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 16 Sep 2018 20:32:29 +0200 Subject: [PATCH 7/7] updated netteForms.min.js --- src/assets/netteForms.min.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/assets/netteForms.min.js b/src/assets/netteForms.min.js index a364c61e7..0eb72b0ef 100644 --- a/src/assets/netteForms.min.js +++ b/src/assets/netteForms.min.js @@ -2,20 +2,21 @@ (function(e,p){if(e.JSON)if("function"===typeof define&&define.amd)define(function(){return p(e)});else if("object"===typeof module&&"object"===typeof module.exports)module.exports=p(e);else{var d=!e.Nette||!e.Nette.noInit;e.Nette=p(e);d&&e.Nette.initOnLoad()}})("undefined"!==typeof window?window:this,function(e){function p(a){return function(b){return a.call(this,b)}}var d={formErrors:[],version:"2.4",addEvent:function(a,b,c){"DOMContentLoaded"===b&&"loading"!==a.readyState?c.call(this):a.addEventListener? a.addEventListener(b,c):"DOMContentLoaded"===b?a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&c.call(this)}):a.attachEvent("on"+b,p(c))},getValue:function(a){var b;if(a){if(a.tagName){if("radio"===a.type){var c=a.form.elements;for(b=0;bb?null: c[b].value;for(b=0;b=b},maxLength:function(a,b,c){if("number"===a.type){if(a.validity.tooLong)return!1; +a.getAttribute&&b===a.getAttribute("data-nette-empty-value")&&(b="");return b},validateControl:function(a,b,c,f,q){a=a.tagName?a:a[0];b=b||d.parseJSON(a.getAttribute("data-nette-rules"));f=void 0===f?{value:d.getEffectiveValue(a)}:f;for(var g=0,k=b.length;g=b},maxLength:function(a,b,c){if("number"===a.type){if(a.validity.tooLong)return!1; if(a.validity.badInput)return null}return c.length<=b},length:function(a,b,c){if("number"===a.type){if(a.validity.tooShort||a.validity.tooLong)return!1;if(a.validity.badInput)return null}b=d.isArray(b)?b:[b,b];return(null===b[0]||c.length>=b[0])&&(null===b[1]||c.length<=b[1])},email:function(a,b,c){return/^("([ !#-[\]-~]|\\[ -~])+"|[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*)@([0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)+[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?$/i.test(c)}, url:function(a,b,c,d){/^[a-z\d+.-]+:/.test(c)||(c="http://"+c);return/^https?:\/\/((([-_0-9a-z\u00C0-\u02FF\u0370-\u1EFF]+\.)*[0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)?[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]{3,39}\])(:\d{1,5})?(\/\S*)?$/i.test(c)?(d.value=c,!0):!1},regexp:function(a,b,c){a="string"===typeof b?b.match(/^\/(.*)\/([imu]*)$/): -!1;try{return a&&(new RegExp(a[1],a[2].replace("u",""))).test(c)}catch(f){}},pattern:function(a,b,c){try{return"string"===typeof b?(new RegExp("^(?:"+b+")$")).test(c):null}catch(f){}},integer:function(a,b,c){return"number"===a.type&&a.validity.badInput?!1:/^-?[0-9]+$/.test(c)},"float":function(a,b,c,d){if("number"===a.type&&a.validity.badInput)return!1;c=c.replace(/ +/g,"").replace(/,/g,".");return/^-?[0-9]*\.?[0-9]+$/.test(c)?(d.value=c,!0):!1},min:function(a,b,c){if("number"===a.type){if(a.validity.rangeUnderflow)return!1; -if(a.validity.badInput)return null}return null===b||parseFloat(c)>=b},max:function(a,b,c){if("number"===a.type){if(a.validity.rangeOverflow)return!1;if(a.validity.badInput)return null}return null===b||parseFloat(c)<=b},range:function(a,b,c){if("number"===a.type){if(a.validity.rangeUnderflow||a.validity.rangeOverflow)return!1;if(a.validity.badInput)return null}return d.isArray(b)?(null===b[0]||parseFloat(c)>=b[0])&&(null===b[1]||parseFloat(c)<=b[1]):null},submitted:function(a){return a.form["nette-submittedBy"]=== -a},fileSize:function(a,b,c){if(e.FileList)for(a=0;ab)return!1;return!0},image:function(a,b,c){if(e.FileList&&c instanceof e.FileList)for(a=0;a=b},max:function(a,b,c){if("number"===a.type){if(a.validity.rangeOverflow)return!1;if(a.validity.badInput)return null}return null===b||parseFloat(c)<=b},range:function(a, +b,c){if("number"===a.type){if(a.validity.rangeUnderflow||a.validity.rangeOverflow)return!1;if(a.validity.badInput)return null}return d.isArray(b)?(null===b[0]||parseFloat(c)>=b[0])&&(null===b[1]||parseFloat(c)<=b[1]):null},submitted:function(a){return a.form["nette-submittedBy"]===a},fileSize:function(a,b,c){if(e.FileList)for(a=0;ab)return!1;return!0},image:function(a,b,c){if(e.FileList&&c instanceof e.FileList)for(a=0;a