├── .babelrc ├── .eslintignore ├── .eslintrc ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── actions │ └── setup │ │ └── action.yml └── workflows │ ├── docs.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── MIGRATING.md ├── README.md ├── commitlint.config.js ├── examples ├── .babelrc ├── .eslintrc ├── README.md ├── devServer.js ├── example.gif ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App │ │ ├── App.jsx │ │ ├── Demo.jsx │ │ ├── PageNotFound.jsx │ │ ├── ResultCode.jsx │ │ ├── index.js │ │ └── prism.js │ ├── Changelog │ │ └── Changelog.jsx │ ├── CreateNumberMask │ │ ├── CreateNumberMask.jsx │ │ └── CreateNumberMask.md │ ├── CreateTextMask │ │ ├── CreateTextMask.jsx │ │ └── CreateTextMask.md │ ├── GettingStarted │ │ ├── GettingStarted.jsx │ │ └── GettingStarted.md │ ├── Migrating │ │ └── Migrating.jsx │ ├── MoreExamples │ │ ├── MoreExamples.jsx │ │ └── MoreExamples.md │ └── index.jsx ├── webpack.config.dev.js └── webpack.config.prod.js ├── package-lock.json ├── package.json ├── src ├── __tests__ │ ├── createNumberMask.test.js │ ├── createTextMask.test.js │ └── utils.test.js ├── createNumberMask.js ├── createTextMask.js ├── defaultMaskDefinitions.js ├── index.js ├── typings.d.ts └── utils.js └── webpack.config.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "es2015"] 3 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.config*.js 2 | node_modules 3 | dist 4 | **/__tests__/** -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "prettier"], 3 | "rules": { 4 | "max-len": [ 5 | "error", 6 | { 7 | "code": 80, 8 | "tabWidth": 2 9 | } 10 | ], 11 | "arrow-parens": ["error", "as-needed"], 12 | "consistent-return": "off", 13 | "no-useless-escape": "off", 14 | "default-case": "off" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This document contains guidelines for contributing to `redux-form-input-masks`. 4 | 5 | Thanks for taking your time and considering contributing to this project :tada:. 6 | 7 | Feel free to open a pull request and propose changes to this document. 8 | 9 | ## Contents 10 | 11 | [Code of conduct](#code-of-conduct) 12 | 13 | [How can I contribute?](#how-can-i-contribute) 14 | 15 | * [Reporting bugs](#reporting-bugs) 16 | * [Suggesting enhancements](#suggesting-enhancements) 17 | * [Your first code contribution](#your-first-code-contribution) 18 | * [Pull requests](#pull-requests) 19 | 20 | ## Code of conduct 21 | 22 | This project and everyone participating in it is governed by this [Code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code and report unacceptable behavior to [renato.bohler@gmail.com](mailto:renato.bohler@gmail.com). 23 | 24 | ## How can I contribute? 25 | 26 | ### Reporting bugs 27 | 28 | If you've found a bug on `redux-form-input-masks`, follow these steps: 29 | 30 | * check if anyone have already reported it [on our opened issues](https://github.com/renato-bohler/redux-form-input-masks/issues). If you find an already existing issue reporting a similar bug, you should comment on this issue instead of creating a new one; 31 | * if there's no similar issues opened, please do [open a new issue](https://github.com/renato-bohler/redux-form-input-masks/issues/new), using a clear and descriptive title and filling the [issue template](ISSUE_TEMPLATE.md) with as many details as possible. 32 | 33 | ### Suggesting enhancements 34 | 35 | Any ideas for new masks or new options for the existing ones are welcome! If you have a suggestion, you can open an issue, repeating the steps described at the [Reporting bugs](#reporting-bugs) section. 36 | 37 | ### Your first code contribution 38 | 39 | There's some things that you really should know before contributing with your awesome coding skills. 40 | 41 | * **starting:** to start, clone the project into your machine and run `npm install`. You can locally run the [`redux-form-input-masks` documentations & examples](https://renato-bohler.github.io/redux-form-input-masks/) to test your changes by running `cd examples && npm install & npm start`; 42 | * **commitlint:** in order to use [`semantic-release`](https://github.com/semantic-release/semantic-release), we follow the [`conventional-changelog`](https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/master/convention.md) commit messages conventions. You need to make sure that all of your commit messages are complying with these conventions, **otherwise your commit will fail**. But don't worry! To simplify this process, we have configured [`commitizen`'s cli](https://github.com/commitizen/cz-cli) onto the project: to commit your stashed changes, run `npm run commit`. If you have `commitizen` installed globally, you can run `git cz`; 43 | * **eslint and prettier**: if you don't have ESLint and Prettier extensions installed for your editor of choice, you definitely should before contributing. They will help you improve your coding in many ways; 44 | * **tests:** make sure your changes haven't broken any tests by running `npm test`. If you open a pull request with broken tests, GitHub will block the pull request from being merged; 45 | * **code coverage**: please do you best to keep the code coverage as high as possible (100% is the goal); 46 | * **good first issue**: if you're looking for a good first issue, there's a label for that. You can use [this link](https://github.com/renato-bohler/redux-form-input-masks/labels/good%20first%20issue) to check all issues marked with this label; 47 | * **documentation**: you have fixed an issue, you ran the tests and everything is fine. Nice, but hold on! Always remember to keep the documentation updated, as this is **very** important. You can update the docs by changing the `examples` folder. If your code makes it to the `master` branch, GitHub Actions will take care of building and deploying the updated documentation to GitHub Pages on the repository. 48 | 49 | ### Pull requests 50 | 51 | When you open a pull request, it will trigger the [Test workfflow on GitHub Actions](https://github.com/renato-bohler/redux-form-input-masks/actions/workflows/test.yml), which will in turn trigger [codecov](http://codecov.io/) and other scripts. Codecov will post a comment with the test coverage report for your changes (absolute and diff values). 52 | 53 | Currently, we have no pull request template, but please be as descriptive as you can and be patient while waiting for review. 54 | 55 | And hey! You can always open a pull request adding a template if you will. 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What are you reporting? 2 | 3 | 4 | 5 | * [ ] Bug 6 | * [ ] Feature request 7 | * [ ] Code refactor 8 | * [ ] Continuous Integration (CI) improvement 9 | * [ ] Changes in documentation (docs) 10 | * [ ] Other (describe) 11 | 12 | ### What is the current behavior? 13 | 14 | 15 | 16 | ### What is the expected behavior? 17 | 18 | 19 | 20 | ### Sandbox Link 21 | 22 | 27 | 28 | ### What's your environment? 29 | 30 | 38 | 39 | ### Other information 40 | 41 | 45 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Sets up Node.js, npm and caches `node_modules` 3 | 4 | inputs: 5 | install-dependencies: 6 | description: When 'true', dependencies will be installed 7 | default: 'true' 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - name: Setup Node.js 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: 16 16 | cache: 'npm' 17 | - name: Install dependencies 18 | if: ${{ inputs.install-dependencies == 'true' }} 19 | run: npm install 20 | shell: sh 21 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | release: 5 | types: 6 | - released 7 | 8 | concurrency: 9 | group: 'docs' 10 | 11 | permissions: 12 | contents: read 13 | pages: write 14 | 15 | jobs: 16 | build_docs: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: ./.github/actions/setup 21 | - name: Build docs 22 | run: npm run build:docs 23 | - name: Upload GitHub Pages artifact 24 | uses: actions/upload-pages-artifact@v1 25 | with: 26 | path: docs 27 | 28 | deploy_docs: 29 | runs-on: ubuntu-latest 30 | needs: build_docs 31 | env: 32 | name: github-pages 33 | steps: 34 | - name: Deploy to GitHub Pages 35 | id: deployment 36 | uses: actions/deploy-pages@v2 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | concurrency: 8 | group: 'release' 9 | 10 | permissions: 11 | contents: write 12 | issues: write 13 | pull-requests: write 14 | id-token: write 15 | 16 | jobs: 17 | test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: ./.github/actions/setup 22 | - name: Test 23 | run: npm run test 24 | - uses: codecov/codecov-action@v3 25 | with: 26 | token: ${{ secrets.CODECOV_TOKEN }} 27 | 28 | publish_package: 29 | runs-on: ubuntu-latest 30 | needs: test 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: ./.github/actions/setup 34 | - name: Publish package 35 | run: | 36 | npm run build 37 | npx semantic-release 38 | env: 39 | GIT_EMAIL: ${{ vars.GIT_EMAIL }} 40 | GIT_USERNAME: ${{ vars.GIT_USERNAME }} 41 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 42 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | 44 | 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: ./.github/actions/setup 18 | - name: Test 19 | run: npm run test 20 | - uses: codecov/codecov-action@v3 21 | 22 | build_package: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: ./.github/actions/setup 27 | - name: Build package 28 | run: npm run build 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | 3 | # Logs 4 | logs 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Optional npm cache directory 10 | .npm 11 | 12 | # Dependency directories 13 | node_modules/ 14 | jspm_packages/ 15 | bower_components/ 16 | 17 | # Yarn Integrity file 18 | .yarn-integrity 19 | 20 | # Optional eslint cache 21 | .eslintcache 22 | 23 | # dotenv environment variables file(s) 24 | .env 25 | .env.* 26 | 27 | # Test generated 28 | coverage/ 29 | 30 | # Build generated 31 | dist/ 32 | docs/ 33 | 34 | # Serverless generated files 35 | .serverless/ 36 | 37 | ### SublimeText ### 38 | # cache files for sublime text 39 | *.tmlanguage.cache 40 | *.tmPreferences.cache 41 | *.stTheme.cache 42 | 43 | # workspace files are user-specific 44 | *.sublime-workspace 45 | 46 | # project files should be checked into the repository, unless a significant 47 | # proportion of contributors will probably not be using SublimeText 48 | # *.sublime-project 49 | 50 | ### VisualStudioCode ### 51 | .vscode/* 52 | !.vscode/settings.json 53 | !.vscode/tasks.json 54 | !.vscode/launch.json 55 | !.vscode/extensions.json 56 | 57 | ### Vim ### 58 | *.sw[a-p] 59 | 60 | ### WebStorm/IntelliJ ### 61 | /.idea 62 | modules.xml 63 | *.ipr 64 | 65 | ### System Files ### 66 | .DS_Store 67 | 68 | # Windows thumbnail cache files 69 | Thumbs.db 70 | ehthumbs.db 71 | ehthumbs_vista.db 72 | 73 | # Folder config file 74 | Desktop.ini 75 | 76 | # Recycle Bin used on file shares 77 | $RECYCLE.BIN/ 78 | 79 | # Thumbnails 80 | ._* 81 | 82 | # Files that might appear in the root of a volume 83 | .DocumentRevisions-V100 84 | .fseventsd 85 | .Spotlight-V100 86 | .TemporaryItems 87 | .Trashes 88 | .VolumeIcon.icns 89 | .com.apple.timemachine.donotpresent 90 | 91 | # Misc 92 | .DS_Store 93 | .env.local 94 | .env.development.local 95 | .env.test.local 96 | .env.production.local 97 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .eslint* 3 | .prettierrc 4 | .babelrc 5 | webpack.config.prod.js 6 | commitlint.config.js 7 | package-lock.json 8 | yarn.lock 9 | coverage 10 | .github 11 | examples 12 | src -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [2.0.2](https://github.com/renato-bohler/redux-form-input-masks/compare/v2.0.1...v2.0.2) (2022-02-17) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * **types:** incompatibility from React-Form and Project ([894045a](https://github.com/renato-bohler/redux-form-input-masks/commit/894045a)) 8 | 9 | 10 | 11 | ## [2.0.1](https://github.com/renato-bohler/redux-form-input-masks/compare/v2.0.0...v2.0.1) (2019-02-28) 12 | 13 | ### Development 14 | 15 | * adds TypeScript definitions ([6062a4b3](https://github.com/renato-bohler/redux-form-input-masks/commit/6062a4b3)) 16 | 17 | 18 | 19 | # [2.0.0](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.3.0...v2.0.0) (2019-02-25) 20 | 21 | ### Bug Fixes 22 | 23 | * **createNumberMask:** changes the default empty value to null ([d2f83f7](https://github.com/renato-bohler/redux-form-input-masks/commit/d2f83f7)), closes [#37](https://github.com/renato-bohler/redux-form-input-masks/issues/37) [#37](https://github.com/renato-bohler/redux-form-input-masks/issues/37) 24 | 25 | ### BREAKING CHANGES 26 | 27 | * **createNumberMask:** default empty value is now null instead of empty string 28 | 29 | 30 | 31 | # [1.3.0](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.2.0...v1.3.0) (2018-11-06) 32 | 33 | ### Features 34 | 35 | * **createTextMask:** adds allowEmpty option ([07e713a](https://github.com/renato-bohler/redux-form-input-masks/commit/07e713a)), closes [#53](https://github.com/renato-bohler/redux-form-input-masks/issues/53) 36 | 37 | 38 | 39 | # [1.2.0](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.5...v1.2.0) (2018-07-23) 40 | 41 | ### Bug Fixes 42 | 43 | * **createNumberMask:** adds validation for the multiplier option ([dcca4a1](https://github.com/renato-bohler/redux-form-input-masks/commit/dcca4a1)) 44 | 45 | ### Features 46 | 47 | * **createNumberMask:** adds multiplier option ([8e4ea2b](https://github.com/renato-bohler/redux-form-input-masks/commit/8e4ea2b)), closes [#46](https://github.com/renato-bohler/redux-form-input-masks/issues/46) 48 | 49 | 50 | 51 | ## [1.1.5](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.4...v1.1.5) (2018-06-26) 52 | 53 | ### Bug Fixes 54 | 55 | * **CreateNumberMask.js:** allow full select delete ([8aa92c9](https://github.com/renato-bohler/redux-form-input-masks/commit/8aa92c9)), closes [#43](https://github.com/renato-bohler/redux-form-input-masks/issues/43) 56 | 57 | 58 | 59 | ## [1.1.4](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.3...v1.1.4) (2018-05-24) 60 | 61 | ### Bug Fixes 62 | 63 | * **createTextMask:** fixes onCompletePattern and onChange calls for non stripped masks ([b9569dd](https://github.com/renato-bohler/redux-form-input-masks/commit/b9569dd)), closes [#39](https://github.com/renato-bohler/redux-form-input-masks/issues/39) 64 | 65 | 66 | 67 | ## [1.1.3](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.2...v1.1.3) (2018-05-12) 68 | 69 | ### Bug Fixes 70 | 71 | * **allowEmpty:** return empty string also when value is empty string ([894deeb](https://github.com/renato-bohler/redux-form-input-masks/commit/894deeb)) 72 | 73 | 74 | 75 | ## [1.1.2](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.1...v1.1.2) (2018-05-08) 76 | 77 | ### Bug Fixes 78 | 79 | * **createTextMask:** fixes onCompletePattern and onChange not being called sometimes ([5081b55](https://github.com/renato-bohler/redux-form-input-masks/commit/5081b55)), closes [#28](https://github.com/renato-bohler/redux-form-input-masks/issues/28) [#25](https://github.com/renato-bohler/redux-form-input-masks/issues/25) 80 | 81 | 82 | 83 | ## [1.1.1](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.1.0...v1.1.1) (2018-05-05) 84 | 85 | ### Bug Fixes 86 | 87 | * **createTextMask:** fixes backspace not controlling caret position correctly on some cases ([4efad32](https://github.com/renato-bohler/redux-form-input-masks/commit/4efad32)), closes [#27](https://github.com/renato-bohler/redux-form-input-masks/issues/27) 88 | 89 | 90 | 91 | # [1.1.0](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.0.1...v1.1.0) (2018-05-03) 92 | 93 | ### Features 94 | 95 | * **CreateNumberMask:** added option allowEmpty ([17cf160](https://github.com/renato-bohler/redux-form-input-masks/commit/17cf160)), closes [#29](https://github.com/renato-bohler/redux-form-input-masks/issues/29) 96 | 97 | 98 | 99 | ## [1.0.1](https://github.com/renato-bohler/redux-form-input-masks/compare/v1.0.0...v1.0.1) (2018-03-14) 100 | 101 | ### Bug Fixes 102 | 103 | * removes babel-polyfill from the bundle ([3c99cd7](https://github.com/renato-bohler/redux-form-input-masks/commit/3c99cd7)), closes [#22](https://github.com/renato-bohler/redux-form-input-masks/issues/22) 104 | 105 | 106 | 107 | # [1.0.0](https://github.com/renato-bohler/redux-form-input-masks/compare/v0.4.1...v1.0.0) (2018-03-12) 108 | 109 | ### Chores 110 | 111 | * first official release :tada: ([ddc0082](https://github.com/renato-bohler/redux-form-input-masks/commit/ddc0082)) 112 | 113 | ### BREAKING CHANGES 114 | 115 | * First official release (v1.0.0) :tada: 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Renato Böhler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MIGRATING.md: -------------------------------------------------------------------------------- 1 | # Migration guide 2 | 3 | ## From 1.x.x to 2.x.x 4 | 5 | A breaking change had to happen on `redux-form-input-masks@2.0.0` in order to fix both [#37](https://github.com/renato-bohler/redux-form-input-masks/issues/37) and [#64](https://github.com/renato-bohler/redux-form-input-masks/issues/64). The reason is explained on [this comment](https://github.com/renato-bohler/redux-form-input-masks/issues/37#issuecomment-398935472). 6 | 7 | Basically, for `createNumberMask`, if the `allowEmpty` message is set to `true`, the empty value at version `2.x.x` will be `null` instead of `undefined` (or empty string). If you never used `createNumberMask`'s `allowEmpty` option, you should be fine to migrate, otherwise you'll need to test if there's anything broken. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-form-input-masks 2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |