mirror of
				https://github.com/CorentinTh/it-tools.git
				synced 2025-10-31 20:13:52 +00:00 
			
		
		
		
	Compare commits
	
		
			91 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5bd5d2c2f3 | ||
|  | 55dffa5635 | ||
|  | baee5e6656 | ||
|  | df5804c71d | ||
|  | 8e99a0a783 | ||
|  | 6e22b12494 | ||
|  | aaa154d0b8 | ||
|  | 37b28c8d21 | ||
|  | 06aac238b2 | ||
|  | a282c526c1 | ||
|  | 19eafdbe37 | ||
|  | 635552fbce | ||
|  | 7f8404645d | ||
|  | 66c569f886 | ||
|  | f02a816eaa | ||
|  | f0d8a3ad26 | ||
|  | f91a2a1343 | ||
|  | 845d106927 | ||
|  | 25bbf9b5f3 | ||
|  | 853e24ca75 | ||
|  | 41fadc7334 | ||
|  | 19ab6d06c3 | ||
|  | d92c46c60b | ||
|  | ed176c7b8c | ||
|  | e152651a4b | ||
|  | a20858dfb8 | ||
|  | b75603f311 | ||
|  | d27c133a66 | ||
|  | bd64eb9dbe | ||
|  | ac54250e8a | ||
|  | e0e7715ce6 | ||
|  | aa3a424f10 | ||
|  | 03e073d4f0 | ||
|  | cb0e3f91db | ||
|  | c8c0dceb21 | ||
|  | ba5e07d063 | ||
|  | 9e94a985ca | ||
|  | a14a7338f8 | ||
|  | b65472ce96 | ||
|  | 87f269a792 | ||
|  | 27f957a098 | ||
|  | 551738f559 | ||
|  | 8f7b7f628e | ||
|  | 46514cf71b | ||
|  | 78157b4cb5 | ||
|  | bf4260f926 | ||
|  | 9fc566e55a | ||
|  | 507d961cd8 | ||
|  | 0631a7f750 | ||
|  | 99ffe08d01 | ||
|  | fcc1335f97 | ||
|  | 3988d33218 | ||
|  | ecb4b1bb7b | ||
|  | b63c4b5415 | ||
|  | 26bda68f41 | ||
|  | f9f80b4e3a | ||
|  | f50c5cb91b | ||
|  | da1da1d269 | ||
|  | 4129169e2b | ||
|  | cffbfe3e39 | ||
|  | 583ce878d2 | ||
|  | c179f6a800 | ||
|  | 7944fd9f17 | ||
|  | 84763e1d5f | ||
|  | ab56a3b598 | ||
|  | b7d7454df5 | ||
|  | b730c1d1b4 | ||
|  | f6da2a44e5 | ||
|  | 89713c4986 | ||
|  | 3401fa4cf1 | ||
|  | 2a7abceba1 | ||
|  | cd9a2318fd | ||
|  | 0ebe60e21e | ||
|  | 634147f0b3 | ||
|  | 318691837e | ||
|  | 17f3e7de35 | ||
|  | aa4fb5ffff | ||
|  | e51a37844a | ||
|  | 6d8db0a5d5 | ||
|  | 84e727a43a | ||
|  | 6f7c399823 | ||
|  | 3777eb941d | ||
|  | dd41bdc57c | ||
|  | 48d0815524 | ||
|  | efe62bb3cf | ||
|  | 48376c17b2 | ||
|  | 458309c563 | ||
|  | fba0701df2 | ||
|  | b013903c41 | ||
|  | 432ac7dd1c | ||
|  | 12a0d93c85 | 
							
								
								
									
										46
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,7 +1,49 @@ | ||||
| # Changelog | ||||
| All notable changes to this project will be documented in this file. | ||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||
|  | ||||
| The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. | ||||
| ## Next | ||||
| - [feat] [Crontab friendly generator](https://it-tools.tech/crontab-generator) | ||||
|  | ||||
| ## 1.6.0 | ||||
| - [feat] [BIP39 generator](https://it-tools.tech/bip39-generator) | ||||
| - [feat] [Base 64 converter](https://it-tools.tech/base64-string-converter) | ||||
|  | ||||
| ## 1.5.2 | ||||
| - [feat] [humans.txt](https://it-tools.tech/humans.txt) | ||||
| - [feat] pwa auto update on new changes | ||||
|  | ||||
| ## 1.5.1 | ||||
| - [feat] switched back to history mode (no more '#' in url) | ||||
|  | ||||
| ## 1.5.0 | ||||
| - [feat] added [qr-code generator](https://it-tools.tech/qrcode-generator) | ||||
|  | ||||
| ## 1.4.0 | ||||
| - [ui] condensed + colored sidenav | ||||
| - [feat] added [git memo](https://it-tools.tech/git-memo) | ||||
| - [refactor] changed app title | ||||
|  | ||||
| ## 1.3.0 | ||||
| - [fix] [GithubContributors] ordered contributors by contribution count | ||||
| - [refactor] used vue-typecasting for number inputs | ||||
| - [feat] lazy loading tools routes | ||||
| - [feat] added [markdown editor](https://it-tools.tech/markdown-editor) | ||||
| - [feat] added [lorem ipsum generator](https://it-tools.tech/lorem-ipsum-generator) | ||||
|  | ||||
| ## 1.2.1 | ||||
| - [fix] [UuidGenerator] added quantity validation rules | ||||
| - [refactor] better isInt checker | ||||
|  | ||||
| ## 1.2.0 | ||||
| - [feat] [UuidGenerator] can generate multiple uuids  | ||||
|  | ||||
| ## 1.1.0 | ||||
| - [feat] 404 route + page | ||||
| - [feat] changelog in the About page  | ||||
| - [feat] contributors list in the About page  | ||||
| - [fix] [ColorConverter] color picker now updates fields  | ||||
|  | ||||
| ## 1.0.1 | ||||
| - [chore] added changelog | ||||
| @@ -9,4 +51,4 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve | ||||
| - [fix] remove history move (incompatible with vercel.com) | ||||
|  | ||||
| ## 1.0.0 | ||||
| - First release | ||||
| - First release | ||||
|   | ||||
							
								
								
									
										46
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| Aggregated set of useful tools that every developer may need once in a while. Available [here](https://it-tools.tech). | ||||
|  | ||||
| ## Functionality/roadmap | ||||
| ## Functionalities roadmap | ||||
| Here is an unordered list of the current functionalities, and some that may come.  | ||||
|  | ||||
| - [x] Token generator | ||||
| @@ -15,14 +15,23 @@ Here is an unordered list of the current functionalities, and some that may come | ||||
| - [x] Url encoder | ||||
| - [x] Base 64 generator | ||||
| - [x] Text information | ||||
| - [ ] Lorem ipsum text generator | ||||
| - [x] Markdown editor | ||||
| - [x] Lorem ipsum text generator | ||||
| - [x] Git memo (cheat sheet) | ||||
| - [x] QR code generator | ||||
| - [x] Bip39 pass-phrase generator | ||||
| - [x] Base 64 string converter | ||||
| - [x] Crontab friendly generator | ||||
| - [ ] CSS memo (cheat sheet) | ||||
| - [ ] REGEX memo (cheat sheet) + tester? | ||||
| - [ ] Minify/un-minify | ||||
| - [ ] Image exif editor/remover | ||||
| - [ ] QR code generator | ||||
| - [ ] Bip39 pass-phrase generator | ||||
| - [ ] Crontab friendly generator | ||||
| - [ ] Image format converter? | ||||
| - [ ] Image cropper  | ||||
| - [ ] 404 page | ||||
| - [ ] Image resizer  | ||||
| - [ ] HTTP client (w/ axios + cors proxy) | ||||
| - [ ] Math expression evaluator | ||||
| - [ ] Math expression graph | ||||
|  | ||||
| You have an idea of a tool? Submit a feature request! | ||||
|  | ||||
| @@ -45,10 +54,33 @@ npm run lint | ||||
| ## Contribute | ||||
| **Pull requests are welcome !** Feel free to contribute. | ||||
|  | ||||
| ### Add a tool | ||||
| To add a tool you just have to create a vue component in [src/routes/tools](./src/routes/tools), example: | ||||
| ```vue | ||||
| <template> | ||||
|     <v-card class="single-card"> | ||||
|         <v-card-title>My component</v-card-title> | ||||
|         <v-card-text>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</v-card-text> | ||||
|     </v-card> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         name: "My component" | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
| </style> | ||||
| ``` | ||||
|  | ||||
| Then, update the file [router.js](./src/router.js) specifying info of the component. | ||||
| Use [fontawesome 5](https://fontawesome.com/icons?d=gallery&m=free) for icons. | ||||
|  | ||||
| ## Credits | ||||
| Coded with ❤️ by [Corentin Thomasset](//corentin-thomasset.fr). | ||||
|  | ||||
| This project is continuously deployed using [vercel.com](https://vercel.com). | ||||
|  | ||||
| ## License | ||||
| This project is under the [MIT license](LICENSE). | ||||
| This project is under the [MIT license](LICENSE). | ||||
|   | ||||
							
								
								
									
										107
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										107
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "it-tools", | ||||
|   "version": "1.0.1", | ||||
|   "version": "1.7.0", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @@ -2483,6 +2483,24 @@ | ||||
|         "file-uri-to-path": "1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "bip39": { | ||||
|       "version": "3.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", | ||||
|       "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", | ||||
|       "requires": { | ||||
|         "@types/node": "11.11.6", | ||||
|         "create-hash": "^1.1.0", | ||||
|         "pbkdf2": "^3.0.9", | ||||
|         "randombytes": "^2.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "@types/node": { | ||||
|           "version": "11.11.6", | ||||
|           "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", | ||||
|           "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "bluebird": { | ||||
|       "version": "3.7.2", | ||||
|       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", | ||||
| @@ -3023,7 +3041,6 @@ | ||||
|       "version": "1.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", | ||||
|       "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "inherits": "^2.0.1", | ||||
|         "safe-buffer": "^5.0.1" | ||||
| @@ -3139,6 +3156,12 @@ | ||||
|           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", | ||||
|           "dev": true | ||||
|         }, | ||||
|         "highlight.js": { | ||||
|           "version": "9.18.1", | ||||
|           "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", | ||||
|           "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", | ||||
|           "dev": true | ||||
|         }, | ||||
|         "supports-color": { | ||||
|           "version": "7.1.0", | ||||
|           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", | ||||
| @@ -3676,7 +3699,6 @@ | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", | ||||
|       "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "cipher-base": "^1.0.1", | ||||
|         "inherits": "^2.0.1", | ||||
| @@ -3689,7 +3711,6 @@ | ||||
|       "version": "1.1.7", | ||||
|       "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", | ||||
|       "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "cipher-base": "^1.0.3", | ||||
|         "create-hash": "^1.1.0", | ||||
| @@ -3699,6 +3720,16 @@ | ||||
|         "sha.js": "^2.4.8" | ||||
|       } | ||||
|     }, | ||||
|     "cron-validator": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/cron-validator/-/cron-validator-1.1.1.tgz", | ||||
|       "integrity": "sha512-vfZb05w/wezuwPZBDvdIBmJp2BvuJExHeyKRa5oBqD2ZDXR61hb3QgPc/3ZhBEQJlAy8Jlnn5XC/JCT3IDqxwg==" | ||||
|     }, | ||||
|     "cronstrue": { | ||||
|       "version": "1.96.0", | ||||
|       "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-1.96.0.tgz", | ||||
|       "integrity": "sha512-kNuuN0AXqspm+jlpwiecFSFBrG5dYBaL5EaW5MEjGAh4IFT/jbKwKCNhzWb8N5NulKQUHEDN7w98GjoAm6PmMg==" | ||||
|     }, | ||||
|     "cross-spawn": { | ||||
|       "version": "6.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", | ||||
| @@ -4373,6 +4404,11 @@ | ||||
|         "domelementtype": "1" | ||||
|       } | ||||
|     }, | ||||
|     "dompurify": { | ||||
|       "version": "2.0.11", | ||||
|       "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.11.tgz", | ||||
|       "integrity": "sha512-qVoGPjIW9IqxRij7klDQQ2j6nSe4UNWANBhZNLnsS7ScTtLb+3YdxkRY8brNTpkUiTtcXsCJO+jS0UCDfenLuA==" | ||||
|     }, | ||||
|     "domutils": { | ||||
|       "version": "1.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", | ||||
| @@ -6214,7 +6250,6 @@ | ||||
|       "version": "3.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", | ||||
|       "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "inherits": "^2.0.1", | ||||
|         "safe-buffer": "^5.0.1" | ||||
| @@ -6253,12 +6288,6 @@ | ||||
|       "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "highlight.js": { | ||||
|       "version": "9.18.1", | ||||
|       "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", | ||||
|       "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "hmac-drbg": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", | ||||
| @@ -6628,8 +6657,7 @@ | ||||
|     "inherits": { | ||||
|       "version": "2.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", | ||||
|       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" | ||||
|     }, | ||||
|     "inquirer": { | ||||
|       "version": "7.1.0", | ||||
| @@ -7913,9 +7941,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "lodash": { | ||||
|       "version": "4.17.15", | ||||
|       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", | ||||
|       "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", | ||||
|       "version": "4.17.19", | ||||
|       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", | ||||
|       "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "lodash._reinterpolate": { | ||||
| @@ -8103,11 +8131,15 @@ | ||||
|         "object-visit": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "marked": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/marked/-/marked-1.1.0.tgz", | ||||
|       "integrity": "sha512-EkE7RW6KcXfMHy2PA7Jg0YJE1l8UPEZE8k45tylzmZM30/r1M1MUXWQfJlrSbsTeh7m/XTwHbWUENvAJZpp1YA==" | ||||
|     }, | ||||
|     "md5.js": { | ||||
|       "version": "1.3.5", | ||||
|       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", | ||||
|       "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "hash-base": "^3.0.0", | ||||
|         "inherits": "^2.0.1", | ||||
| @@ -9084,7 +9116,6 @@ | ||||
|       "version": "3.0.17", | ||||
|       "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", | ||||
|       "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "create-hash": "^1.1.2", | ||||
|         "create-hmac": "^1.1.4", | ||||
| @@ -9980,6 +10011,11 @@ | ||||
|       "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "qrcode.vue": { | ||||
|       "version": "1.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-1.7.0.tgz", | ||||
|       "integrity": "sha512-R7t6Y3fDDtcU7L4rtqwGUDP9xD64gJhIwpfjhRCTKmBoYF6SS49PIJHRJ048cse6OI7iwTwgyy2C46N9Ygoc6g==" | ||||
|     }, | ||||
|     "qs": { | ||||
|       "version": "6.5.2", | ||||
|       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", | ||||
| @@ -10018,7 +10054,6 @@ | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", | ||||
|       "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "safe-buffer": "^5.1.0" | ||||
|       } | ||||
| @@ -10051,6 +10086,29 @@ | ||||
|         "unpipe": "1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "raw-loader": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.1.tgz", | ||||
|       "integrity": "sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "loader-utils": "^2.0.0", | ||||
|         "schema-utils": "^2.6.5" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "loader-utils": { | ||||
|           "version": "2.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", | ||||
|           "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", | ||||
|           "dev": true, | ||||
|           "requires": { | ||||
|             "big.js": "^5.2.2", | ||||
|             "emojis-list": "^3.0.0", | ||||
|             "json5": "^2.1.2" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "read-pkg": { | ||||
|       "version": "5.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", | ||||
| @@ -10428,7 +10486,6 @@ | ||||
|       "version": "2.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", | ||||
|       "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "hash-base": "^3.0.0", | ||||
|         "inherits": "^2.0.1" | ||||
| @@ -10475,8 +10532,7 @@ | ||||
|     "safe-buffer": { | ||||
|       "version": "5.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||||
|       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | ||||
|     }, | ||||
|     "safe-regex": { | ||||
|       "version": "1.1.0", | ||||
| @@ -10737,7 +10793,6 @@ | ||||
|       "version": "2.4.11", | ||||
|       "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", | ||||
|       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "inherits": "^2.0.1", | ||||
|         "safe-buffer": "^5.0.1" | ||||
| @@ -12718,9 +12773,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "websocket-extensions": { | ||||
|       "version": "0.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", | ||||
|       "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", | ||||
|       "version": "0.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", | ||||
|       "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "which": { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "it-tools", | ||||
|   "description": "", | ||||
|   "version": "1.0.1", | ||||
|   "version": "1.7.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "serve": "vue-cli-service serve", | ||||
| @@ -10,9 +10,15 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "axios": "^0.19.2", | ||||
|     "bip39": "^3.0.2", | ||||
|     "color-convert": "^2.0.1", | ||||
|     "color-name": "^1.1.4", | ||||
|     "core-js": "^3.6.4", | ||||
|     "cron-validator": "^1.1.1", | ||||
|     "cronstrue": "^1.96.0", | ||||
|     "dompurify": "^2.0.11", | ||||
|     "marked": "^1.1.0", | ||||
|     "qrcode.vue": "^1.7.0", | ||||
|     "register-service-worker": "^1.7.1", | ||||
|     "roboto-fontface": "*", | ||||
|     "vue": "^2.6.11", | ||||
| @@ -36,6 +42,7 @@ | ||||
|     "less": "^3.0.4", | ||||
|     "less-loader": "^5.0.0", | ||||
|     "lint-staged": "^9.5.0", | ||||
|     "raw-loader": "^4.0.1", | ||||
|     "sass": "^1.19.0", | ||||
|     "sass-loader": "^8.0.0", | ||||
|     "vue-cli-plugin-vuetify": "~2.0.5", | ||||
|   | ||||
							
								
								
									
										5
									
								
								public/humans.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								public/humans.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| /* TEAM */ | ||||
| Developer: Corentin Thomasset | ||||
| Site: https://github.com/CorentinTh | ||||
| Twitter: @cthmsst | ||||
|  | ||||
| @@ -5,7 +5,8 @@ | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||||
|     <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||||
|     <title><%= htmlWebpackPlugin.options.title %></title> | ||||
|     <title>IT Tools - Set of handy developer tools</title> | ||||
|     <link rel="canonical" href="https://it-tools.tech"> | ||||
|     <meta itemprop="name" content="IT-Tools"> | ||||
|     <meta property="og:title" content="IT-Tools"> | ||||
|     <meta name="twitter:title" content="IT-Tools"> | ||||
| @@ -16,6 +17,7 @@ | ||||
|     <meta itemprop="image" content="/img/banner.png"> | ||||
|     <meta property="og:image" content="/img/banner.png"> | ||||
|     <meta name="twitter:image" content="/img/banner.png"> | ||||
|     <link rel="author" href="humans.txt" /> | ||||
|   </head> | ||||
|   <body> | ||||
|     <noscript> | ||||
|   | ||||
							
								
								
									
										72
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								src/App.vue
									
									
									
									
									
								
							| @@ -1,23 +1,24 @@ | ||||
| <template> | ||||
|     <v-app id="inspire"> | ||||
|         <vue-headful | ||||
|             :title="currentRoute ? `${currentRoute.text} - IT-Tools` : 'IT-Tools'" | ||||
|             :description="currentRoute ? currentRoute.description: 'Aggregated set of useful tools that every developer may need once in a while.'" | ||||
|             :keywords="currentRoute ? currentRoute.keywords: null" | ||||
|             image="/img/banner.png" | ||||
|                 :title="currentRoute ? `${currentRoute.text} - IT Tools` : 'IT Tools - Set of handy developer tools'" | ||||
|                 :description="currentRoute ? currentRoute.description: 'Aggregated set of useful tools that every developer may need once in a while.'" | ||||
|                 :keywords="currentRoute ? currentRoute.keywords: null" | ||||
|                 image="/img/banner.png" | ||||
|         /> | ||||
|         <v-navigation-drawer v-model="drawer" app clipped> | ||||
|             <template v-slot:prepend> | ||||
|                 <SearchBar class="hidden-sm-and-up"/> | ||||
|             </template> | ||||
|  | ||||
|             <SearchBar class="hidden-sm-and-up" /> | ||||
|  | ||||
|             <v-list dense> | ||||
|             <v-list dense id="navigation-list"> | ||||
|  | ||||
|                 <div v-for="section in items" :key="section.title"> | ||||
|                     <v-subheader class="mt-4 pl-4">{{section.title}}</v-subheader> | ||||
|  | ||||
|                     <v-list-item v-for="item in section.child" :key="item.text" :to="item.path"> | ||||
|                         <v-list-item-action> | ||||
|                             <v-icon>{{ item.icon }}</v-icon> | ||||
|                             <v-icon style="width: 1.25em">{{ item.icon }}</v-icon> | ||||
|                         </v-list-item-action> | ||||
|                         <v-list-item-content> | ||||
|                             <v-list-item-title> | ||||
| @@ -99,7 +100,7 @@ | ||||
|             appVersion: 'v' + process.env.APPLICATION_VERSION, | ||||
|             drawer: null, | ||||
|             items: toolsComponents, | ||||
|             currentRoute:{} | ||||
|             currentRoute: {} | ||||
|         }), | ||||
|         mounted() { | ||||
|             this.setTitle() | ||||
| @@ -107,14 +108,14 @@ | ||||
|         created() { | ||||
|             this.$vuetify.theme.dark = true | ||||
|         }, | ||||
|         methods:{ | ||||
|             setTitle(){ | ||||
|         methods: { | ||||
|             setTitle() { | ||||
|                 const path = this.$router.currentRoute.path; | ||||
|                 this.currentRoute = toolsComponents.map(p => p.child).flat().find(p => p.path === path) | ||||
|             } | ||||
|         }, | ||||
|         watch:{ | ||||
|             '$route'(){ | ||||
|         watch: { | ||||
|             '$route'() { | ||||
|                 this.setTitle() | ||||
|             } | ||||
|         } | ||||
| @@ -125,6 +126,51 @@ | ||||
|     html { | ||||
|         overflow-y: auto !important; | ||||
|     } | ||||
|     code{ | ||||
|         background-color: rgba(0, 0, 0, 0.15) !important; | ||||
|         box-shadow: none !important; | ||||
|         color: #9a9a9a !important; | ||||
|         font-weight: normal !important; | ||||
|     } | ||||
|     .pretty-scrollbar{ | ||||
|         &::-webkit-scrollbar { | ||||
|             width: 5px!important; | ||||
|             height: 5px !important; | ||||
|         } | ||||
|  | ||||
|         /* Track */ | ||||
|         &::-webkit-scrollbar-track { | ||||
|             opacity: 0 !important; | ||||
|         } | ||||
|  | ||||
|         /* Handle */ | ||||
|         &::-webkit-scrollbar-thumb { | ||||
|             background: rgba(241, 241, 241, 0.10) !important; | ||||
|             border-radius: 10px; | ||||
|         } | ||||
|  | ||||
|         /* Handle on hover */ | ||||
|         &::-webkit-scrollbar-thumb:hover { | ||||
|             background: rgba(241, 241, 241, 0.20)!important; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #navigation-list{ | ||||
|         div:first-child .v-subheader{ | ||||
|                 margin-top: 0 !important; | ||||
|         } | ||||
|  | ||||
|         .v-list-item__action{ | ||||
|             margin: 8px 25px 8px 0; | ||||
|             .v-icon{ | ||||
|                 color: #4CAF50 !important; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .v-navigation-drawer__content{ | ||||
|         .pretty-scrollbar; | ||||
|     } | ||||
|  | ||||
|     .single-card { | ||||
|         width: 100%; | ||||
|   | ||||
| @@ -4,7 +4,9 @@ | ||||
|         <p class="text-justify"> | ||||
|             Welcome to <strong>IT-Tools</strong>! This wonderful website, originally created with ❤ by | ||||
|             <a href="//corentin-thomasset.fr">Corentin Thomasset</a>, aggregate a set of useful tools | ||||
|             that every developer may need once in a while. | ||||
|             that every developer may need once in a while. And don't forget to add <strong>IT-Tools</strong> to your | ||||
|             shortcut bar (press <code>{{ isMacOS ? 'Cmd' : 'Ctrl' }} + | ||||
|             D</code>). | ||||
|         </p> | ||||
|  | ||||
|  | ||||
| @@ -30,7 +32,10 @@ | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         name: "Abstract" | ||||
|         name: "Abstract", | ||||
|         data: () => ({ | ||||
|             isMacOS: navigator.platform.toUpperCase().indexOf('MAC') >= 0 | ||||
|         }) | ||||
|     } | ||||
| </script> | ||||
|  | ||||
|   | ||||
							
								
								
									
										58
									
								
								src/components/ColorInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/components/ColorInput.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <template> | ||||
|     <v-text-field v-model="color" hide-details class="ma-0 pa-0" outlined :label="label" v-on:input="$emit('input', color)"> | ||||
|         <template v-slot:append> | ||||
|             <v-menu v-model="menu" top nudge-bottom="101" nudge-left="16" :close-on-content-click="false"> | ||||
|                 <template v-slot:activator="{ on }"> | ||||
|                     <div :style="swatchStyle" v-on="on" /> | ||||
|                 </template> | ||||
|                 <v-card> | ||||
|                     <v-card-text class="pa-0"> | ||||
|                         <v-color-picker v-model="color" flat v-on:input="$emit('input', color)"/> | ||||
|                     </v-card-text> | ||||
|                 </v-card> | ||||
|             </v-menu> | ||||
|         </template> | ||||
|     </v-text-field> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     // From: https://codepen.io/JamieCurnow/pen/KKPjraK | ||||
|  | ||||
|     export default { | ||||
|         name: "ColorInput", | ||||
|         props:{ | ||||
|             value: { | ||||
|                 type: String, | ||||
|                 default: '#FFFFFF' | ||||
|             }, | ||||
|             label:String | ||||
|         }, | ||||
|         data: () => ({ | ||||
|             menu: false, | ||||
|             color:'' | ||||
|         }), | ||||
|         mounted() { | ||||
|             this.color = this.value | ||||
|         }, | ||||
|         computed: { | ||||
|             swatchStyle() { | ||||
|                 const { color, menu } = this | ||||
|                 return { | ||||
|                     backgroundColor: color, | ||||
|                     cursor: 'pointer', | ||||
|                     height: '30px', | ||||
|                     width: '30px', | ||||
|                     borderRadius: menu ? '50%' : '4px', | ||||
|                     transition: 'border-radius 200ms ease-in-out' | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep .v-input__append-inner{ | ||||
|         margin-top: 13px; | ||||
|     } | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										43
									
								
								src/components/CopyableCodeContent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/components/CopyableCodeContent.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <template> | ||||
|     <div class="copyable-code-content" @click="copy($slots.default[0].text)"> | ||||
|         <pre class="pretty-scrollbar"><slot></slot></pre> | ||||
|         <v-icon>far fa-copy</v-icon> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import {copyable} from "../mixins/copyable.mixin"; | ||||
|  | ||||
|     export default { | ||||
|         name: "CopyableCodeContent", | ||||
|         mixins: [copyable] | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style lang="less" scoped> | ||||
|     .copyable-code-content { | ||||
|         cursor: pointer; | ||||
|         background-color: rgba(0, 0, 0, 0.1); | ||||
|         border-radius: 4px; | ||||
|         padding: 8px 15px; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|  | ||||
|         pre { | ||||
|             flex: 1; | ||||
|             overflow-x: auto; | ||||
|         } | ||||
|  | ||||
|         .v-icon { | ||||
|             opacity: 0; | ||||
|         } | ||||
|  | ||||
|         &:hover { | ||||
|             .v-icon { | ||||
|                 opacity: 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										49
									
								
								src/components/GithubContributors.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/components/GithubContributors.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| <template> | ||||
|     <div class="github-contributor"> | ||||
|  | ||||
|         <div v-if="loading" class="text-center pt-3 pb-3"> | ||||
|             <v-progress-circular indeterminate /> | ||||
|         </div> | ||||
|  | ||||
|         <v-list v-else class="pa-0"> | ||||
|             <v-list-item v-for="(contributor, i) in contributors" :key="i" :href="contributor.html_url"> | ||||
|                 <v-list-item-avatar> | ||||
|                     <v-img :src="contributor.avatar_url"></v-img> | ||||
|                 </v-list-item-avatar> | ||||
|                 <v-list-item-content> | ||||
|                     {{contributor.login}} | ||||
|                 </v-list-item-content> | ||||
|             </v-list-item> | ||||
|         </v-list> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     const baseUrl = 'https://api.github.com/repos/$repo$/contributors' | ||||
|     import axios from 'axios' | ||||
|  | ||||
|     export default { | ||||
|         name: "GithubContributors", | ||||
|         props: ['repo'], | ||||
|         data: () => ({ | ||||
|             contributors: [], | ||||
|             loading: true, | ||||
|             hasError: false | ||||
|         }), | ||||
|         mounted() { | ||||
|             const url = baseUrl.replace('$repo$', this.repo) | ||||
|  | ||||
|             axios | ||||
|                 .get(url) | ||||
|                 .then(({data}) => { | ||||
|                     this.contributors = data.sort((a, b) => b.contributions - a.contributions) | ||||
|                     this.loading = false | ||||
|                 }) | ||||
|                 .catch(() => this.hasError = true) | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										73
									
								
								src/components/MemoViewer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/components/MemoViewer.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <template> | ||||
|     <div class="memo-viewer" v-bind:style="{ columns: `auto ${colWidth}` }"> | ||||
|         <div class="section" v-for="(group,i) in memo" :key="i"> | ||||
|             <h2>{{group.section}}</h2> | ||||
|  | ||||
|             <div class="tip" v-for="(tips,i) in group.child" :key="i"> | ||||
|                 <v-card> | ||||
|                     <v-card-text> | ||||
|                         <template v-for="tip in (Array.isArray(tips) ? tips : [tips])"> | ||||
|                             <p :key="tip.text">{{tip.text}}</p> | ||||
|                             <CopyableCodeContent class="code" :key="tip.code">{{tip.code}}</CopyableCodeContent> | ||||
|                         </template> | ||||
|                     </v-card-text> | ||||
|                 </v-card> | ||||
|                 </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import CopyableCodeContent from "./CopyableCodeContent"; | ||||
|  | ||||
|     export default { | ||||
|         name: "MemoViewer", | ||||
|         props: { | ||||
|             memo: Array, | ||||
|             colWidth: { | ||||
|                 type: String, | ||||
|                 default: '400px' | ||||
|             } | ||||
|         }, | ||||
|         components: { | ||||
|             CopyableCodeContent | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style lang="less" scoped> | ||||
|     .memo-viewer { | ||||
|         column-gap: 30px; | ||||
|         column-rule: 1px solid #37373961; | ||||
|         column-fill: auto; | ||||
|  | ||||
|     } | ||||
|     .section { | ||||
|         break-inside: avoid-column; | ||||
|         display: inline-block; | ||||
|         margin-bottom: 20px; | ||||
|         width: 100%; | ||||
|  | ||||
|         h2 { | ||||
|             margin: 25px 0 15px; | ||||
|             display: inline-block; | ||||
|         } | ||||
|  | ||||
|         .tip { | ||||
|             margin: 20px 0; | ||||
|  | ||||
|             .v-card{ | ||||
|                 background-color: rgba(47, 46, 46, 0.44); | ||||
|             } | ||||
|  | ||||
|             p { | ||||
|                 margin-bottom: 10px; | ||||
|  | ||||
|                 &:not(:first-child){ | ||||
|                     margin-top: 16px; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										10
									
								
								src/mixins/copyable.mixin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/mixins/copyable.mixin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import {copyToClipboard} from "../utils/helpers"; | ||||
|  | ||||
| export const copyable = { | ||||
|     methods: { | ||||
|         copy(text, toastText = 'Copied to clipboard !'){ | ||||
|             copyToClipboard(text); | ||||
|             this.$toast.success(toastText) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -5,6 +5,12 @@ import router from "../router"; | ||||
| if(process.env.VUE_APP_GANALYTICS){ | ||||
|     Vue.use(VueAnalytics, { | ||||
|         id: process.env.VUE_APP_GANALYTICS, | ||||
|         router | ||||
|         router, | ||||
|         set:[ | ||||
|             { | ||||
|                 field: 'dimension1', | ||||
|                 value: process.env.APPLICATION_VERSION | ||||
|             } | ||||
|         ] | ||||
|     }) | ||||
| } | ||||
|   | ||||
| @@ -20,7 +20,8 @@ if (process.env.NODE_ENV === 'production') { | ||||
|       console.log('New content is downloading.') | ||||
|     }, | ||||
|     updated () { | ||||
|       console.log('New content is available; please refresh.') | ||||
|       console.log('New content is available; hard refresh.'); | ||||
|       window.location.reload(true); | ||||
|     }, | ||||
|     offline () { | ||||
|       console.log('No internet connection found. App is running in offline mode.') | ||||
|   | ||||
							
								
								
									
										105
									
								
								src/router.js
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								src/router.js
									
									
									
									
									
								
							| @@ -1,20 +1,9 @@ | ||||
| import Vue from 'vue' | ||||
| import VueRouter from 'vue-router' | ||||
| import Home from './routes/Home.vue' | ||||
| import TokenGenerator from "./routes/tools/TokenGenerator"; | ||||
| import Hash from "./routes/tools/Hash"; | ||||
| import DateConverter from "./routes/tools/DateConverter"; | ||||
| import UrlEncoder from "./routes/tools/UrlEncoder"; | ||||
| import FileToBase64 from "./routes/tools/FileToBase64"; | ||||
| import TextCypher from "./routes/tools/TextCypher"; | ||||
| import TextStats from "./routes/tools/TextStats"; | ||||
| import BaseConverter from "./routes/tools/BaseConverter"; | ||||
| import UuidGenerator from "./routes/tools/UuidGenerator"; | ||||
| import ColorConverter from "./routes/tools/ColorConverter"; | ||||
|  | ||||
| Vue.use(VueRouter) | ||||
|  | ||||
|  | ||||
| const toolsComponents = [ | ||||
|     { | ||||
|         title: 'Crypto', | ||||
| @@ -23,7 +12,7 @@ const toolsComponents = [ | ||||
|                 icon: 'fa-key', | ||||
|                 text: 'Token generator', | ||||
|                 path: '/token-generator', | ||||
|                 component: TokenGenerator, | ||||
|                 component: () => import('./routes/tools/TokenGenerator'), | ||||
|                 keywords: ['token', 'random', 'string', 'alphanumeric'], | ||||
|                 description: 'Generate random tokens.' | ||||
|             }, | ||||
| @@ -31,14 +20,14 @@ const toolsComponents = [ | ||||
|                 icon: 'fa-fingerprint', | ||||
|                 text: 'Uuid generator', | ||||
|                 path: '/uuid-generator', | ||||
|                 component: UuidGenerator, | ||||
|                 component: () => import('./routes/tools/UuidGenerator'), | ||||
|                 keywords: ['token', 'v4', 'string', 'alphanumeric'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-font', | ||||
|                 text: 'Hash text', | ||||
|                 path: '/hash', | ||||
|                 component: Hash, | ||||
|                 component: () => import('./routes/tools/Hash'), | ||||
|                 keywords: ['md5', 'sha1', 'sha256', 'sha224', 'sha512', 'sha384', 'sha3', 'ripemd160', 'random'] | ||||
|  | ||||
|             }, | ||||
| @@ -46,9 +35,16 @@ const toolsComponents = [ | ||||
|                 icon: 'fa-lock', | ||||
|                 text: 'Cypher/uncypher text', | ||||
|                 path: '/cypher', | ||||
|                 component: TextCypher, | ||||
|                 component: () => import('./routes/tools/TextCypher'), | ||||
|                 keywords: ['aes', 'tripledes', 'rabbit', 'rabbitlegacy', 'rc4'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-file-word', | ||||
|                 text: 'BIP39 passphrase generator', | ||||
|                 path: '/bip39-generator', | ||||
|                 component: () => import('./routes/tools/BIP39Generator'), | ||||
|                 keywords: [] | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
| @@ -58,21 +54,21 @@ const toolsComponents = [ | ||||
|                 icon: 'fa-calendar', | ||||
|                 text: 'Date/Time converter', | ||||
|                 path: '/date-converter', | ||||
|                 component: DateConverter, | ||||
|                 component: () => import('./routes/tools/DateConverter'), | ||||
|                 keywords: ['locale', 'format', 'iso 8601', 'utc', 'timestamp', 'unix', 'year', 'month', 'day', 'hours', 'minutes', 'seconds'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-exchange-alt', | ||||
|                 text: 'Base converter', | ||||
|                 path: '/base-converter', | ||||
|                 component: BaseConverter, | ||||
|                 component: () => import('./routes/tools/BaseConverter'), | ||||
|                 keywords: ['binary', 'hexadecimal', 'decimal'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-palette', | ||||
|                 text: 'Color picker/converter', | ||||
|                 path: '/color-picker-converter', | ||||
|                 component: ColorConverter, | ||||
|                 component: () => import('./routes/tools/ColorConverter'), | ||||
|                 keywords: ['rgb', 'rgba', 'hexadecimal', 'hsla', 'red', 'green', 'blue', 'alpha'] | ||||
|             }, | ||||
|         ], | ||||
| @@ -84,28 +80,79 @@ const toolsComponents = [ | ||||
|                 icon: 'fa-link', | ||||
|                 text: 'URL encode/decode', | ||||
|                 path: '/url-encoder', | ||||
|                 component: UrlEncoder, | ||||
|                 component: () => import('./routes/tools/UrlEncoder'), | ||||
|                 keywords: ['%20'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-file-export', | ||||
|                 text: 'File to Base64', | ||||
|                 path: '/file-to-base64', | ||||
|                 component: FileToBase64 | ||||
|                 component: () => import('./routes/tools/FileToBase64') | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-file-alt', | ||||
|                 text: 'Base64 string converter', | ||||
|                 path: '/base64-string-converter', | ||||
|                 component: () => import('./routes/tools/StringToBase64') | ||||
|             } | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         title: 'Text', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-align-left', | ||||
|                 text: 'Text stats', | ||||
|                 path: '/text-stats', | ||||
|                 component: () => import('./routes/tools/TextStats'), | ||||
|                 keywords: ['word', 'count', 'size', 'bytes', 'length'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fab fa-markdown', | ||||
|                 text: 'Markdown editor', | ||||
|                 path: '/markdown-editor', | ||||
|                 component: () => import('./routes/tools/MarkdownEditor'), | ||||
|                 keywords: ['text', 'html', 'markdown'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-align-justify', | ||||
|                 text: 'Lorem ipsum generator', | ||||
|                 path: '/lorem-ipsum-generator', | ||||
|                 component: () => import('./routes/tools/LoremIpsumGenerator'), | ||||
|                 keywords: ['text', 'dolor', 'sit', 'placeholder', 'fill', 'dummy'] | ||||
|             } | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         title: 'Memos', | ||||
|         child: [ | ||||
|             { | ||||
|                 text: 'Git memo', | ||||
|                 path: '/git-memo', | ||||
|                 icon: 'fa-code-branch', | ||||
|                 component: () => import('./routes/tools/GitMemo'), | ||||
|                 keywords: ['git', 'push', 'rebase', 'merge', 'tag', 'commit', 'checkout'] | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         title: 'Miscellaneous', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-align-left\n', | ||||
|                 text: 'Text stats', | ||||
|                 path: '/text-stats', | ||||
|                 component: TextStats, | ||||
|                 keywords: ['word', 'count', 'size', 'bytes', 'length'] | ||||
|                 text: 'Crontab generator', | ||||
|                 path: '/crontab-generator', | ||||
|                 icon: 'fa-clock', | ||||
|                 component: () => import('./routes/tools/CrontabGenerator'), | ||||
|                 keywords: ['Cron', 'Schedule'] | ||||
|             }, | ||||
|         ], | ||||
|             { | ||||
|                 text: 'QR Code generator', | ||||
|                 path: '/qrcode-generator', | ||||
|                 icon: 'fa-qrcode', | ||||
|                 component: () => import('./routes/tools/QRCodeGenerator'), | ||||
|                 keywords: [] | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ]; | ||||
|  | ||||
| @@ -121,11 +168,17 @@ const routes = [ | ||||
|         path: '/about', | ||||
|         name: 'About', | ||||
|         component: () => import('./routes/About.vue') | ||||
|     }, | ||||
|     { | ||||
|         path: '*', | ||||
|         name: '404', | ||||
|         component: () => import('./routes/NotFound.vue') | ||||
|     } | ||||
| ] | ||||
|  | ||||
| const router = new VueRouter({ | ||||
|     base: process.env.BASE_URL, | ||||
|     mode: 'history', | ||||
|     routes | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,63 @@ | ||||
| <template> | ||||
|   <v-card class="single-card"> | ||||
|     <v-card-title>About</v-card-title> | ||||
|     <v-card-text> | ||||
|       <Abstract /> | ||||
|     </v-card-text> | ||||
|   </v-card> | ||||
|     <div> | ||||
|         <v-row justify="center" align="center"> | ||||
|             <v-col cols="12" xl="12"> | ||||
|                 <v-card class="single-card"> | ||||
|                     <v-card-title>About</v-card-title> | ||||
|                     <v-card-text> | ||||
|                         <Abstract/> | ||||
|                     </v-card-text> | ||||
|                 </v-card> | ||||
|             </v-col> | ||||
|  | ||||
|         </v-row> | ||||
|         <v-row justify="center"> | ||||
|             <v-col cols="12" md="5" sm="12"> | ||||
|                 <v-card> | ||||
|                     <v-card-title>Contributors</v-card-title> | ||||
|                     <github-contributors repo="CorentinTh/it-tools"/> | ||||
|                 </v-card> | ||||
|             </v-col> | ||||
|             <v-col cols="12" md="7" sm="12"> | ||||
|                 <v-card> | ||||
|                     <v-card-title>Changelog</v-card-title> | ||||
|                     <v-card-text> | ||||
|                         <div v-html="changelog" class="changelog"> | ||||
|                         </div> | ||||
|                     </v-card-text> | ||||
|                 </v-card> | ||||
|             </v-col> | ||||
|         </v-row> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import Abstract from "../components/Abstract"; | ||||
|     import Abstract from "../components/Abstract"; | ||||
|     import GithubContributors from "../components/GithubContributors"; | ||||
|     import changelog from "../../CHANGELOG.md" | ||||
|     import marked from 'marked' | ||||
|     import DOMPurify from 'dompurify'; | ||||
|  | ||||
|   export default { | ||||
|     name: "About", | ||||
|     components : { | ||||
|       Abstract | ||||
|     }, | ||||
|   } | ||||
| </script> | ||||
|     export default { | ||||
|         name: "About", | ||||
|         data: () => ({ | ||||
|             changelog: [] | ||||
|         }), | ||||
|         mounted() { | ||||
|             this.changelog = DOMPurify.sanitize(marked('##' + changelog.replace(/^(.*?)##/s, ''))); | ||||
|         }, | ||||
|         components: { | ||||
|             Abstract, | ||||
|             GithubContributors | ||||
|         }, | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep { | ||||
|         .changelog { | ||||
|             h2 { | ||||
|                 margin-top: 10px; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
| @@ -9,9 +9,7 @@ | ||||
|                     </v-card-text> | ||||
|                 </v-card> | ||||
|             </v-col> | ||||
|         </v-row> | ||||
|         <v-row justify="center" align="center"> | ||||
|             <v-col cols="12" lg="8" md="12"> | ||||
|             <v-col cols="12" lg="5" md="12"> | ||||
|                 <v-card class="card-auto"> | ||||
|                     <v-card-text> | ||||
|                         <div class="card-wrapper "> | ||||
| @@ -56,7 +54,7 @@ | ||||
|         flex-wrap: wrap; | ||||
|  | ||||
|         div { | ||||
|             flex: 0 1 20%; | ||||
|             flex: 0 1 33%; | ||||
|  | ||||
|             @media only screen and (max-width: 800px) { | ||||
|                 flex: 0 1 33%; | ||||
|   | ||||
							
								
								
									
										54
									
								
								src/routes/NotFound.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/routes/NotFound.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <template> | ||||
|     <div class="e404"> | ||||
|         <div class="e404-image"> | ||||
|             <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 100 511.999 300" xml:space="preserve"> | ||||
|                 <g><path d="M140.61,273.063h-5.956v-22.69c0-11.623-10.593-19.433-26.358-19.433s-26.357,7.81-26.357,19.433v22.69H65.571 l25.677-100.707c1.101-2.746,1.194-5.749,1.186-8.392l-0.001-0.284c0-15.632-22.966-20.737-30.881-20.737 c-10.132,0-18.414,5.148-22.723,14.123c-0.199,0.415-0.359,0.847-0.481,1.29C0,298.98,0,300.981,0,302.305 c0,12.096,8.537,20.876,20.297,20.876H33.97c4.2,0,7.604-3.405,7.604-7.604c0-4.199-3.404-7.604-7.604-7.604H20.297 c-3.184,0-4.887-1.694-5.072-5.04c1.954-8.608,23.256-87.252,37.593-139.831c1.785-3.288,4.721-4.954,8.734-4.954 c5.289,0,14.355,3.338,15.673,5.732l0.001,0.128c0.001,0.584,0.006,2.084-0.132,2.756c-0.178,0.397-0.321,0.809-0.43,1.231 L48.417,278.788c-0.58,2.275-0.076,4.69,1.365,6.544c1.44,1.854,3.656,2.938,6.004,2.938h33.757c4.2,0,7.604-3.405,7.604-7.604 v-30.294c0-2.528,5.766-4.225,11.149-4.225c5.481,0,11.15,1.581,11.15,4.225v30.294c0,4.199,3.404,7.604,7.604,7.604h13.56 c2.252,0,4.226,4.671,4.226,9.996c0,5.013-3.022,9.171-4.382,9.708H127.05c-4.2,0-7.604,3.405-7.604,7.604v34.045 c0,1.954-4.872,4.225-11.15,4.225c-6.467,0-11.149-2.523-11.149-4.225v-34.045c0-4.199-3.404-7.604-7.604-7.604H59.318 c-4.2,0-7.604,3.405-7.604,7.604c0,4.199,3.404,7.604,7.604,7.604h22.621v26.441c0,11.26,11.086,19.433,26.357,19.433 c15.52,0,26.358-7.991,26.358-19.433v-26.441h5.956c9.192,0,19.434-10.232,19.434-24.916 C160.043,283.663,151.87,273.063,140.61,273.063z"/></g> | ||||
|                 <g><path d="M492.566,273.063h-5.956v-22.69c0-11.623-10.593-19.433-26.358-19.433s-26.357,7.81-26.357,19.433v22.69h-16.368 l25.679-100.708c1.1-2.745,1.193-5.75,1.185-8.393l-0.001-0.281c0-15.632-22.966-20.737-30.881-20.737 c-10.132,0-18.415,5.148-22.724,14.123c-0.199,0.415-0.359,0.847-0.481,1.29c0,0-4.869,17.852-11.125,40.898 c0,0.001,0,0.002-0.001,0.003l-7.879,29.071c-19.345,71.527-19.345,72.449-19.345,73.978c0,12.096,8.537,20.876,20.298,20.876 h61.64v26.441c0,11.26,11.085,19.433,26.357,19.433c15.52,0,26.358-7.991,26.358-19.433v-26.441h5.956 c9.192,0,19.434-10.232,19.434-24.915C512,283.663,503.826,273.063,492.566,273.063z M492.41,307.974h-13.404 c-4.2,0-7.604,3.405-7.604,7.604v34.045c0,1.954-4.872,4.225-11.15,4.225c-6.467,0-11.149-2.523-11.149-4.225v-34.045 c0-4.199-3.404-7.604-7.604-7.604h-69.244c-3.183,0-4.887-1.694-5.073-5.037c0.677-2.966,4.201-16.655,18.801-70.634l7.877-29.064 c5.504-20.276,9.934-36.53,10.916-40.133c1.785-3.288,4.721-4.954,8.735-4.954c5.289,0,14.354,3.338,15.673,5.732l0.001,0.124 c0.001,0.585,0.006,2.09-0.133,2.762c-0.177,0.396-0.32,0.808-0.429,1.23l-28.249,110.79c-0.58,2.275-0.076,4.69,1.365,6.544 c1.44,1.854,3.656,2.938,6.004,2.938H441.5c4.2,0,7.604-3.405,7.604-7.604v-30.294c0-2.528,5.766-4.225,11.149-4.225 c5.481,0,11.15,1.581,11.15,4.225v30.294c0,4.199,3.404,7.604,7.604,7.604h13.56c2.252,0,4.226,4.671,4.226,9.996 C496.792,303.281,493.769,307.437,492.41,307.974z"/></g> | ||||
|                 <g><path d="M330.892,206.939c-2.512-3.363-7.279-4.051-10.642-1.536c-3.362,2.515-4.05,7.281-1.535,10.643 c10.168,13.597,15.542,29.786,15.542,46.818c0,43.149-35.104,78.252-78.252,78.252c-43.15,0-78.253-35.104-78.253-78.252 c0-43.149,35.104-78.253,78.253-78.253c17.152,0,33.434,5.444,47.083,15.744c3.353,2.53,8.122,1.863,10.651-1.49 c2.529-3.352,1.862-8.12-1.49-10.65c-16.311-12.307-35.76-18.812-56.245-18.812c-51.534,0-93.461,41.927-93.461,93.461 s41.927,93.46,93.461,93.46c51.534,0,93.46-41.926,93.46-93.46C349.464,242.523,343.042,223.185,330.892,206.939z"/></g> | ||||
|                 <g><path d="M296.624,292.726l-29.863-29.863L296.624,233c2.968-2.969,2.968-7.783-0.002-10.753c-2.971-2.97-7.784-2.97-10.754,0 l-29.863,29.863l-29.863-29.863c-2.971-2.97-7.784-2.97-10.754,0c-2.97,2.97-2.97,7.784,0,10.753l29.863,29.863l-29.863,29.863 c-2.97,2.97-2.97,7.784,0,10.753c1.485,1.484,3.432,2.227,5.378,2.227c1.946,0,3.892-0.742,5.377-2.227l29.863-29.863 l29.863,29.863c1.485,1.484,3.432,2.227,5.377,2.227s3.892-0.742,5.378-2.227C299.593,300.51,299.593,295.695,296.624,292.726z"/></g> | ||||
|             </svg> | ||||
|         </div> | ||||
|         <div class="separator"></div> | ||||
|         <div class="e404-description"> | ||||
|             Page not found, sorry. | ||||
|         </div> | ||||
|         <v-btn color="primary" @click="$router.go(-1)">Back</v-btn> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         name: "404.vue" | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     .e404 { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|  | ||||
|         .e404-image { | ||||
|             width: 200px; | ||||
|  | ||||
|             svg { | ||||
|                 fill: #ffffff; | ||||
|                 opacity: 0.3; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         .separator { | ||||
|             width: 60px; | ||||
|             height: 3px; | ||||
|             border-radius: 5px; | ||||
|             background-color: #4CAF50; | ||||
|         } | ||||
|  | ||||
|         .e404-description { | ||||
|             font-size: 30px; | ||||
|             opacity: 0.9; | ||||
|             margin: 15px 0; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										99
									
								
								src/routes/tools/BIP39Generator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/routes/tools/BIP39Generator.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| <template> | ||||
|     <v-card class="single-card"> | ||||
|         <v-card-title>BIP39 passphrase generator</v-card-title> | ||||
|         <v-card-text> | ||||
|             <v-select | ||||
|                     outlined | ||||
|                     label="Language" | ||||
|                     @change="languageChanged" | ||||
|                     :items="languageList" | ||||
|                     v-model="language" | ||||
|             /> | ||||
|             <v-text-field | ||||
|                     outlined | ||||
|                     v-model="entropy" | ||||
|                     label="Entropy" | ||||
|                     append-icon="fa-clipboard" | ||||
|                     @click:append="copy(entropy)" | ||||
|                     :rules="rules.entropy" | ||||
|                     ref="entropy" | ||||
|             /> | ||||
|             <v-text-field | ||||
|                     outlined | ||||
|                     v-model="passphrase" | ||||
|                     label="Passphrase" | ||||
|                     append-icon="fa-clipboard" | ||||
|                     @click:append="copy(passphrase)" | ||||
|                     :rules="rules.passphrase" | ||||
|                     ref="passphrase" | ||||
|             /> | ||||
|             <div class="text-center"> | ||||
|                 <v-btn @click="refresh">refresh</v-btn> | ||||
|             </div> | ||||
|         </v-card-text> | ||||
|     </v-card> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import * as bip39 from "bip39"; | ||||
|     import {copyable} from "../../mixins/copyable.mixin"; | ||||
|  | ||||
|     const shuffle = (str) => str.split('').sort(() => 0.5 - Math.random()).join(''); | ||||
|  | ||||
|     const getRandomBuffer = () => { | ||||
|         return Buffer.from(shuffle('0123456789abcdef'.repeat(16)).substring(0, 32), 'hex'); | ||||
|     } | ||||
|  | ||||
|     export default { | ||||
|         name: 'BIP39Generator', | ||||
|         mixins: [copyable], | ||||
|         data: () => ({ | ||||
|             buffer: getRandomBuffer(), | ||||
|             languageList: Object.keys(bip39.wordlists).filter(k => !k.match(/[A-Z]{2}/)).map(k => ({text: k.split('_').map(k => k.charAt(0).toUpperCase() + k.slice(1)).join(' '), value:k})), | ||||
|             language: 'english', | ||||
|             rules: { | ||||
|                 passphrase: [ | ||||
|                     v => (!!v && bip39.validateMnemonic(v)) || 'Invalid mnemonic.' | ||||
|                 ], | ||||
|                 entropy: [ | ||||
|                     v => (!!v && !!v.match(/[0-9a-fA-F]{32}/)) || 'Invalid entropy.' | ||||
|                 ] | ||||
|             } | ||||
|         }), | ||||
|         methods:{ | ||||
|             refresh(){ | ||||
|                 this.buffer = getRandomBuffer(); | ||||
|             }, | ||||
|             languageChanged(){ | ||||
|                 bip39.setDefaultWordlist(this.language); | ||||
|                 this.passphrase = bip39.entropyToMnemonic(this.buffer) | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             entropy: { | ||||
|                 get() { | ||||
|                     return this.buffer.toString('hex') | ||||
|                 }, | ||||
|                 set(value) { | ||||
|                     if(this.$refs.entropy.validate()) { | ||||
|                         this.buffer = Buffer.from(value, 'hex') | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             passphrase: { | ||||
|                 get() { | ||||
|                     return bip39.entropyToMnemonic(this.buffer) | ||||
|                 }, | ||||
|                 set(value) { | ||||
|                     if(this.$refs.passphrase.validate()){ | ||||
|                         this.buffer = Buffer.from(bip39.mnemonicToEntropy(value), 'hex') | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
| @@ -8,7 +8,7 @@ | ||||
|                             label="Input base" | ||||
|                             outlined | ||||
|                             type="number" | ||||
|                             v-model="inputBase" | ||||
|                             v-model.number="inputBase" | ||||
|                             ref="inputBase" | ||||
|                             hide-details="auto" | ||||
|                             :rules="baseRules" | ||||
| @@ -33,7 +33,7 @@ | ||||
|                             label="Output base" | ||||
|                             outlined | ||||
|                             type="number" | ||||
|                             v-model="outputBase" | ||||
|                             v-model.number="outputBase" | ||||
|                             ref="outputBase" | ||||
|                             :rules="baseRules" | ||||
|                     /> | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|                             hide-inputs | ||||
|                             mode="rgba" | ||||
|                             v-model="rgbPicker" | ||||
|                             @input="(v) => updateColors(v, 'picker')" | ||||
|                     /> | ||||
|                 </v-col> | ||||
|                 <v-col cols="12" sm="6" align="center"> | ||||
| @@ -116,7 +117,7 @@ | ||||
|                 this.keyword = convert.rgb.keyword(r, g, b); | ||||
|             }, | ||||
|             updateColors(value, fromType) { | ||||
|                 if (this.$refs[fromType].validate()) { | ||||
|                 if (fromType === 'picker' || this.$refs[fromType].validate()) { | ||||
|                     if (fromType === 'rgb') { | ||||
|                         const [r, g, b] = value.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(v => parseInt(v)); | ||||
|                         this.rgbPicker = {r, g, b} | ||||
| @@ -150,6 +151,13 @@ | ||||
|                         } catch (ignored) { | ||||
|                             // ignored | ||||
|                         } | ||||
|                     } else if (fromType === 'picker') { | ||||
|                         const {r, g, b} = value; | ||||
|  | ||||
|                         this.setRGB(r, g, b); | ||||
|                         this.setHEX(r, g, b); | ||||
|                         this.setHSL(r, g, b); | ||||
|                         this.setKeyword(r, g, b); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
							
								
								
									
										221
									
								
								src/routes/tools/CrontabGenerator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/routes/tools/CrontabGenerator.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| <template> | ||||
|     <v-row justify="center" align="center"> | ||||
|         <v-col cols="12" xl="5" lg="5" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-title>Crontab generator</v-card-title> | ||||
|                 <v-card-text> | ||||
|                     <div class="result">{{cronString}}</div> | ||||
|                     <v-text-field | ||||
|                             ref="cron" | ||||
|                             class="cron-wrapper" | ||||
|                             outlined | ||||
|                             v-model="cron" | ||||
|                             label="Cron" | ||||
|                             append-icon="fa-copy" | ||||
|                             @click:append="copy(cron)" | ||||
|                             :rules="[isCronValid]" | ||||
|                             hide-details="auto" | ||||
|                     /> | ||||
|  | ||||
|                     <v-checkbox | ||||
|                             hide-details | ||||
|                             label="Verbose" | ||||
|                             v-model="cronstrueConfig.verbose" | ||||
|                     /> | ||||
|                     <v-checkbox | ||||
|                             hide-details | ||||
|                             label="Use 24 hour time format" | ||||
|                             v-model="cronstrueConfig.use24HourTimeFormat" | ||||
|                     /> | ||||
|                     <v-checkbox | ||||
|                             hide-details | ||||
|                             label="Day of the week start a index 0" | ||||
|                             v-model="cronstrueConfig.dayOfWeekStartIndexZero" | ||||
|                     /> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|         <v-col cols="12" xl="6" lg="7" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-title>Crontab helper</v-card-title> | ||||
|                 <v-card-text> | ||||
|                     <div class="text-center"> | ||||
|                         <pre> | ||||
| ┌────────── day of week (0 - 6, sunday=0) OR sun,mon ... | ||||
| | ┌──────── month (1 - 12) OR jan,feb,mar,apr ... | ||||
| | | ┌────── day of month (1 - 31) | ||||
| | | | ┌──── hour (0 - 23) | ||||
| | | | | ┌── minute (0 - 59) | ||||
| | | | | | | ||||
| * * * * * command | ||||
|                         </pre> | ||||
|                     </div> | ||||
|                     <br> | ||||
|                     <v-simple-table dense> | ||||
|                         <template v-slot:default> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th class="text-left">Symbol</th> | ||||
|                                 <th class="text-left">Meaning</th> | ||||
|                                 <th class="text-left">Example</th> | ||||
|                                 <th class="text-left">Equivalent</th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody> | ||||
|                             <tr> | ||||
|                                 <td>*</td> | ||||
|                                 <td>Any value</td> | ||||
|                                 <td> | ||||
|                                     <pre>* * * *</pre> | ||||
|                                 </td> | ||||
|                                 <td>Every minute</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>-</td> | ||||
|                                 <td>Range of values</td> | ||||
|                                 <td> | ||||
|                                     <pre>1-10 * * *</pre> | ||||
|                                 </td> | ||||
|                                 <td>Minutes 1 through 10</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>,</td> | ||||
|                                 <td>List of values</td> | ||||
|                                 <td> | ||||
|                                     <pre>1,10 * * *</pre> | ||||
|                                 </td> | ||||
|                                 <td>At minutes 1 and 10</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>/</td> | ||||
|                                 <td>Step values</td> | ||||
|                                 <td> | ||||
|                                     <pre>*/10 * * *</pre> | ||||
|                                 </td> | ||||
|                                 <td>Every 10 minutes</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@yearly</td> | ||||
|                                 <td>Once every year at midnight of 1 January</td> | ||||
|                                 <td> | ||||
|                                     <pre>@yearly</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 1 1 *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@annually</td> | ||||
|                                 <td>Same as @yearly</td> | ||||
|                                 <td> | ||||
|                                     <pre>@annually</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 1 1 *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@monthly</td> | ||||
|                                 <td>Once a month at midnight on the first day</td> | ||||
|                                 <td> | ||||
|                                     <pre>@monthly</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 1 * *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@weekly</td> | ||||
|                                 <td>Once a week at midnight on Sunday morning</td> | ||||
|                                 <td> | ||||
|                                     <pre>@weekly</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 * * 0</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@daily</td> | ||||
|                                 <td>Once a day at midnight</td> | ||||
|                                 <td> | ||||
|                                     <pre>@daily</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 * * *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@midnight</td> | ||||
|                                 <td>Same as @daily</td> | ||||
|                                 <td> | ||||
|                                     <pre>@midnight</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 0 * * *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@hourly</td> | ||||
|                                 <td>Once an hour at the beginning of the hour</td> | ||||
|                                 <td> | ||||
|                                     <pre>@hourly</pre> | ||||
|                                 </td> | ||||
|                                 <td>0 * * * *</td> | ||||
|                             </tr> | ||||
|                             <tr> | ||||
|                                 <td>@reboot</td> | ||||
|                                 <td>Run at startup</td> | ||||
|                                 <td></td> | ||||
|                                 <td></td> | ||||
|                             </tr> | ||||
|                             </tbody> | ||||
|                         </template> | ||||
|                     </v-simple-table> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|     </v-row> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import {copyable} from "../../mixins/copyable.mixin"; | ||||
|     import cronstrue from 'cronstrue'; | ||||
|     import * as CronValidator from 'cron-validator' | ||||
|  | ||||
|     export default { | ||||
|         name: "CrontabGenerator", | ||||
|         mixins: [copyable], | ||||
|         data: () => ({ | ||||
|             cron: '* * * * *', | ||||
|             cronstrueConfig: { | ||||
|                 verbose: true, | ||||
|                 dayOfWeekStartIndexZero: true, | ||||
|                 use24HourTimeFormat: true, | ||||
|                 throwExceptionOnParseError: true | ||||
|             } | ||||
|         }), | ||||
|         methods: { | ||||
|             isCronValid(v) { | ||||
|                 return CronValidator.isValidCron(v, {allowBlankDay: true, alias: true}); | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             cronString() { | ||||
|                 if (this.isCronValid(this.cron)) { | ||||
|                     return cronstrue.toString(this.cron, this.cronstrueConfig) | ||||
|                 } else { | ||||
|                     return ' ' | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep .cron-wrapper input { | ||||
|         text-align: center; | ||||
|         font-size: 22px; | ||||
|         font-family: Consolas, monospace; | ||||
|     } | ||||
|  | ||||
|     .result { | ||||
|         text-align: center; | ||||
|         font-size: 18px; | ||||
|         margin-bottom: 22px; | ||||
|     } | ||||
|  | ||||
|     .text-center{ | ||||
|         pre{ | ||||
|             display: inline-block; | ||||
|             text-align: left; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										154
									
								
								src/routes/tools/GitMemo.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/routes/tools/GitMemo.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| <template> | ||||
|     <v-row> | ||||
|         <v-col cols="12" xl="12"> | ||||
|             <v-card> | ||||
|                 <v-card-title>Git Memo</v-card-title> | ||||
|                 <v-card-text> | ||||
|                     <MemoViewer :memo="tips"/> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|     </v-row> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import MemoViewer from "../../components/MemoViewer"; | ||||
|  | ||||
|     export default { | ||||
|         name: "GitMemo", | ||||
|         data: () => ({ | ||||
|             tips: [ | ||||
|                 { | ||||
|                     section: 'Get started', | ||||
|                     child: [ | ||||
|                         { | ||||
|                             text: 'Create a git repo', | ||||
|                             code: 'git init' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Clone an existing repository', | ||||
|                             code: 'git clone [repo url]' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Add current files to next commit', | ||||
|                             code: 'git add .' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Commit tracked files changes', | ||||
|                             code: 'git commit -am "[commit message]"' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'List files that has changed', | ||||
|                             code: 'git status' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'List changes in tracked files', | ||||
|                             code: 'git diff' | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     section: 'Basic configuration', | ||||
|                     child: [ | ||||
|                         { | ||||
|                             text: 'Set the name that will be associated to every operation', | ||||
|                             code: 'git config --global user.name "[nom]"' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Set the email address that will be associated to every operation', | ||||
|                             code: 'git config --global user.email "[email]"' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Tell git to always push tags', | ||||
|                             code: 'git config --global push.followTags true' | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     section: 'I\'ve made a mistake', | ||||
|                     child: [ | ||||
|                         { | ||||
|                             text: 'Change last commit message', | ||||
|                             code: 'git commit --amend' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Undo most recent commit and keep changes', | ||||
|                             code: 'git reset HEAD~1' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Undo most recent commit and get rid of changes', | ||||
|                             code: 'git reset HEAD~1 --hard' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Reset branch to remote state', | ||||
|                             code: 'git fetch origin\ngit reset --hard origin/[branch-name]' | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     section: 'Setup SSH', | ||||
|                     child: [ | ||||
|                         [ | ||||
|                             { | ||||
|                                 text: '1). Generate an SSH key.', | ||||
|                                 code: 'ssh-keygen -t rsa -b 4096 -C "[email]"' | ||||
|                             }, | ||||
|                             { | ||||
|                                 text: '2). Start the ssh-agent in the background.', | ||||
|                                 code: 'eval "$(ssh-agent -s)"' | ||||
|                             }, | ||||
|                             { | ||||
|                                 text: '3). Add your SSH private key to the ssh-agent.', | ||||
|                                 code: 'ssh-add ~/.ssh/id_rsa' | ||||
|                             }, | ||||
|                             { | ||||
|                                 text: '4). Add your SSH public key to your git server (for github: Settings -> SSH and GPG keys)', | ||||
|                                 code: 'cat ~/.ssh/id_rsa.pub' | ||||
|                             }, | ||||
|                             { | ||||
|                                 text: '5). (Optional) Testing your SSH connection', | ||||
|                                 code: 'ssh -T git@github.com' | ||||
|                             }, | ||||
|  | ||||
|                         ] | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     section: 'Merge and rebase', | ||||
|                     child: [ | ||||
|                         { | ||||
|                             text: 'Merge a branch into the current', | ||||
|                             code: 'git merge [branch]' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Abort merge (conflicts)', | ||||
|                             code: 'git merge --abort' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Continue merge after resolving conflicts', | ||||
|                             code: 'git merge --continue' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Rebase a branch into the current', | ||||
|                             code: 'git rebase [branch]' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Rebase merge (conflicts)', | ||||
|                             code: 'git merge --abort' | ||||
|                         }, | ||||
|                         { | ||||
|                             text: 'Continue rebase after resolving conflicts', | ||||
|                             code: 'git merge --continue' | ||||
|                         }, | ||||
|                     ] | ||||
|                 }, | ||||
|             ] | ||||
|         }), | ||||
|         components: { | ||||
|             MemoViewer | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style lang="less" scoped> | ||||
| </style> | ||||
							
								
								
									
										94
									
								
								src/routes/tools/LoremIpsumGenerator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/routes/tools/LoremIpsumGenerator.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| <template> | ||||
|     <v-row justify="center" align="center" class="lorem-ipsum-generator"> | ||||
|         <v-col cols="12" xl="5" lg="6" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-title>Lorem ipsum generator</v-card-title> | ||||
|                 <v-card-text> | ||||
|                     <v-slider v-model="paragraphs" min="1" max="20" label="Paragraphs" thumb-label/> | ||||
|                     <v-range-slider v-model="sentencePerParagraph" min="1" max="50" label="Sentences per paragraph" | ||||
|                                     thumb-label/> | ||||
|                     <v-range-slider v-model="wordPerSentence" min="1" max="50" label="Words per sentence" thumb-label hide-details/> | ||||
|                     <v-checkbox v-model="startWithLoremIpsum" label="Start with 'Lorem ipsum ...'" hide-details/> | ||||
|                     <v-checkbox v-model="asHTML" label="As HTML" hide-details/> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|         <v-col cols="12" xl="5" lg="6" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-text> | ||||
|                     <v-textarea outlined readonly hide-details="auto" v-model="loremIpsum" rows="15" | ||||
|                                 class="text-justify"></v-textarea> | ||||
|                     <div class="text-center mt-4"> | ||||
|                         <v-btn depressed @click="copy()">Copy</v-btn> | ||||
|                     </div> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|     </v-row> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import {copyToClipboard, randFromArray, randIntFromInterval} from "../../utils/helpers"; | ||||
|  | ||||
|     const vocabulary = ['a', 'ac', 'accumsan', 'ad', 'adipiscing', 'aenean', 'aliquam', 'aliquet', 'amet', 'ante', 'aptent', 'arcu', 'at', 'auctor', 'bibendum', 'blandit', 'class', 'commodo', 'condimentum', 'congue', 'consectetur', 'consequat', 'conubia', 'convallis', 'cras', 'cubilia', 'cum', 'curabitur', 'curae', 'dapibus', 'diam', 'dictum', 'dictumst', 'dignissim', 'dolor', 'donec', 'dui', 'duis', 'egestas', 'eget', 'eleifend', 'elementum', 'elit', 'enim', 'erat', 'eros', 'est', 'et', 'etiam', 'eu', 'euismod', 'facilisi', 'faucibus', 'felis', 'fermentum', 'feugiat', 'fringilla', 'fusce', 'gravida', 'habitant', 'habitasse', 'hac', 'hendrerit', 'himenaeos', 'iaculis', 'id', 'imperdiet', 'in', 'inceptos', 'integer', 'interdum', 'ipsum', 'justo', 'lacinia', 'lacus', 'laoreet', 'lectus', 'leo', 'ligula', 'litora', 'lobortis', 'lorem', 'luctus', 'maecenas', 'magna', 'magnis', 'malesuada', 'massa', 'mattis', 'mauris', 'metus', 'mi', 'molestie', 'mollis', 'montes', 'morbi', 'mus', 'nam', 'nascetur', 'natoque', 'nec', 'neque', 'netus', 'nisi', 'nisl', 'non', 'nostra', 'nulla', 'nullam', 'nunc', 'odio', 'orci', 'ornare', 'parturient', 'pellentesque', 'penatibus', 'per', 'pharetra', 'phasellus', 'placerat', 'platea', 'porta', 'porttitor', 'posuere', 'potenti', 'praesent', 'pretium', 'primis', 'proin', 'pulvinar', 'purus', 'quam', 'quis', 'quisque', 'rhoncus', 'ridiculus', 'risus', 'rutrum', 'sagittis', 'sapien', 'scelerisque', 'sed', 'sem', 'semper', 'senectus', 'sit', 'sociis', 'sociosqu', 'sodales', 'sollicitudin', 'suscipit', 'suspendisse', 'taciti', 'tellus', 'tempor', 'tempus', 'tincidunt', 'torquent', 'tortor', 'turpis', 'ullamcorper', 'ultrices', 'ultricies', 'urna', 'varius', 'vehicula', 'vel', 'velit', 'venenatis', 'vestibulum', 'vitae', 'vivamus', 'viverra', 'volutpat', 'vulputate']; | ||||
|     const firstSentence = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; | ||||
|  | ||||
|     const generateSentence = (length) => { | ||||
|         let sentence = Array.from({length}).map(() => randFromArray(vocabulary)).join(' ') | ||||
|         sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1) + '.' | ||||
|         return sentence | ||||
|     } | ||||
|  | ||||
|  | ||||
|     export default { | ||||
|         name: "LoremIpsumGenerator", | ||||
|         data: () => ({ | ||||
|             paragraphs: 1, | ||||
|             sentencePerParagraph: [3, 8], | ||||
|             wordPerSentence: [8, 15], | ||||
|             startWithLoremIpsum: true, | ||||
|             asHTML: false | ||||
|         }), | ||||
|         methods:{ | ||||
|             copy(){ | ||||
|                 copyToClipboard(this.loremIpsum) | ||||
|                 this.$toast.success('Copied to clipboard.') | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             loremIpsum: function () { | ||||
|                 const lorem = Array | ||||
|                     .from({length: this.paragraphs}) | ||||
|                     .map(() => { | ||||
|                         const length = randIntFromInterval(...this.sentencePerParagraph); | ||||
|  | ||||
|                         return Array.from({length}).map(() => { | ||||
|                             const wordCount = randIntFromInterval(...this.wordPerSentence); | ||||
|                             return generateSentence(wordCount); | ||||
|                         }) | ||||
|                     }); | ||||
|  | ||||
|                 if (this.startWithLoremIpsum) { | ||||
|                     lorem[0][0] = firstSentence | ||||
|                 } | ||||
|  | ||||
|                 let result; | ||||
|                 if(this.asHTML){ | ||||
|                     result = `<p>${lorem.map(s => s.join(' ')).join('</p>\n\n<p>')}</p>` | ||||
|                 }else{ | ||||
|                     result = lorem.map(s => s.join(' ')).join('\n\n') | ||||
|                 } | ||||
|  | ||||
|                 return result; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep { | ||||
|         .v-label{ | ||||
|             min-width: 200px !important; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										71
									
								
								src/routes/tools/MarkdownEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/routes/tools/MarkdownEditor.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| <template> | ||||
|     <v-row justify="center" align="center"> | ||||
|         <v-col cols="12" xl="5" lg="6" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-text> | ||||
|                     <v-textarea v-model="markdown" auto-grow outlined label="Markdown editor"/> | ||||
|                     <div class="text-center"> | ||||
|                         <v-btn @click="copy(markdown)">copy markdown</v-btn> | ||||
|                     </div> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|         <v-col cols="12" xl="5" lg="6" md="12"> | ||||
|             <v-card> | ||||
|                 <v-card-text > | ||||
|                     <div class="preview" v-html="html"></div> | ||||
|                     <div class="text-center"> | ||||
|                         <v-divider /> | ||||
|                         <br> | ||||
|                         <v-btn @click="copy(html)">copy html</v-btn> | ||||
|                     </div> | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </v-col> | ||||
|     </v-row> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     // import {debounce} from "../../utils/helpers"; | ||||
|     import marked from 'marked' | ||||
|     import DOMPurify from 'dompurify'; | ||||
|     import {copyToClipboard} from "../../utils/helpers"; | ||||
|  | ||||
|     export default { | ||||
|         name: "MarkdownEditor", | ||||
|         data: () => ({ | ||||
|             markdown: '# Hello, World!\nLorem ipsum **dolor** sit *amet*, consectetur adipisicing elit. A aspernatur commodi consequuntur distinctio dolore doloribus eaque earum est ipsum nobis numquam pariatur perspiciatis quasi quis, sed, sunt tempore tenetur, veniam!\n', | ||||
|         }), | ||||
|         methods: { | ||||
|             copy(text){ | ||||
|                 copyToClipboard(text) | ||||
|                 this.$toast.success('Copied to clipboard.') | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             html() { | ||||
|                 return DOMPurify.sanitize(marked(this.markdown)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep { | ||||
|         .preview { | ||||
|             padding: 20px; | ||||
|  | ||||
|             h1{ | ||||
|                 margin-bottom: 15px; | ||||
|             } | ||||
|             pre { | ||||
|                 width: 100%; | ||||
|  | ||||
|                 code { | ||||
|                     width: 100% !important; | ||||
|                     padding: 10px; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										137
									
								
								src/routes/tools/QRCodeGenerator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/routes/tools/QRCodeGenerator.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| <template> | ||||
|     <v-card class="single-card"> | ||||
|         <v-card-title>QR-code generator</v-card-title> | ||||
|         <v-card-text> | ||||
|             <v-row justify="center" align="center"> | ||||
|                 <v-col cols="12" lg="6" sm="12"> | ||||
|                     <v-text-field | ||||
|                             outlined | ||||
|                             v-model="value" | ||||
|                             label="Data" | ||||
|                             :rules="rules.value" | ||||
|                     /> | ||||
|                     <v-slider v-model="size" min="100" max="1920" label="Size (preview will not change): " thumb-label/> | ||||
|                     <v-select | ||||
|                             outlined | ||||
|                             v-model="level" | ||||
|                             :items="levels" | ||||
|                             label="Error resistance" | ||||
|                     /> | ||||
|                     <v-row> | ||||
|                         <v-col cols="12" md="6" sm="12"> | ||||
|                             <ColorInput v-model="fgcolor" label="Foreground color"/> | ||||
|                         </v-col> | ||||
|                         <v-col cols="12" md="6" sm="12"> | ||||
|                             <ColorInput v-model="bgcolor" label="Background color"/> | ||||
|                         </v-col> | ||||
|                     </v-row> | ||||
|  | ||||
|                 </v-col> | ||||
|  | ||||
|                 <v-col cols="12" lg="6" sm="12" class="text-center"> | ||||
|                     <qrcode-vue | ||||
|                             :value="input" | ||||
|                             :size="size" | ||||
|                             :level="level" | ||||
|                             :background="bgcolor" | ||||
|                             :foreground="fgcolor" | ||||
|                             render-as="svg" | ||||
|                             class-name="qrcode-wrapper" | ||||
|                     /> | ||||
|                 </v-col> | ||||
|             </v-row> | ||||
|  | ||||
|             <div class="text-center mt-3 mb-sm-2"> | ||||
|                 <v-btn @click="download('png')" class="mr-1" color="primary">download as png</v-btn> | ||||
|                 <v-btn @click="download('svg')" class="ml-1" color="primary">download as svg</v-btn> | ||||
|             </div> | ||||
|         </v-card-text> | ||||
|     </v-card> | ||||
|  | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import QrcodeVue from 'qrcode.vue' | ||||
|     import colors from "color-name"; | ||||
|     import ColorInput from "../../components/ColorInput"; | ||||
|     import {downloadBase64File} from "../../utils/helpers"; | ||||
|  | ||||
|     export default { | ||||
|         name: "QRCodeGenerator", | ||||
|         data: () => ({ | ||||
|             value: 'https://it-tools.tech', | ||||
|             size: 300, | ||||
|             level: 'M', | ||||
|             bgcolor: '#ffffff', | ||||
|             fgcolor: '#000000', | ||||
|             levels: [ | ||||
|                 {text: 'Low', value: 'L'}, | ||||
|                 {text: 'Medium', value: 'M'}, | ||||
|                 {text: 'Quartile', value: 'Q'}, | ||||
|                 {text: 'High', value: 'H'} | ||||
|             ], | ||||
|             rules: { | ||||
|                 value: [ | ||||
|                     v => v.length > 0 || 'Value is needed' | ||||
|                 ], | ||||
|                 color: [ | ||||
|                     v => { | ||||
|                         v = v.trim() | ||||
|                         const isFFFFFF = /^#(?:[0-9a-fA-F]{6})$/.test(v); | ||||
|                         const isFFF = /^#(?:[0-9a-fA-F]{3})$/.test(v); | ||||
|                         const isRGB = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.test(v); | ||||
|                         const isHSL = /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/.test(v); | ||||
|                         const isKeyword = v in colors; | ||||
|                         const isTransparent = v === 'transparent'; | ||||
|  | ||||
|                         return isFFFFFF || isFFF || isKeyword || isTransparent || isRGB || isHSL || 'Incorrect color.' | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         }), | ||||
|         methods: { | ||||
|             download(type) { | ||||
|                 const svgEl = this.$el.querySelector('.qrcode-wrapper svg'); | ||||
|                 const svgString = new XMLSerializer().serializeToString(svgEl); | ||||
|                 const svgUrl = `data:image/svg+xml;base64,${btoa(svgString)}`; | ||||
|  | ||||
|                 if (type === 'png') { | ||||
|                     const canvas = document.createElement("canvas"); | ||||
|                     canvas.width = this.size; | ||||
|                     canvas.height = this.size; | ||||
|                     const ctx = canvas.getContext("2d"); | ||||
|  | ||||
|                     const image = new Image(); | ||||
|                     image.onload = function () { | ||||
|                         ctx.drawImage(image, 0, 0); | ||||
|                         const result = canvas.toDataURL(); | ||||
|  | ||||
|                         downloadBase64File(result, 'qr-code'); | ||||
|                     }; | ||||
|                     image.src = svgUrl; | ||||
|                 } else { | ||||
|                     downloadBase64File(svgUrl, 'qr-code'); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             input() { | ||||
|                 return this.value | ||||
|  | ||||
|             } | ||||
|         }, | ||||
|         components: { | ||||
|             QrcodeVue, | ||||
|             ColorInput | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep .qrcode-wrapper { | ||||
|         & > * { | ||||
|             width: 300px !important; | ||||
|             height: 300px !important; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										66
									
								
								src/routes/tools/StringToBase64.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/routes/tools/StringToBase64.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <template> | ||||
|     <v-card class="single-card"> | ||||
|         <v-card-title>Base64 string converter</v-card-title> | ||||
|         <v-card-text> | ||||
|             <v-textarea | ||||
|                     outlined | ||||
|                     v-model="clear" | ||||
|                     label="Clear text" | ||||
|             ></v-textarea> | ||||
|  | ||||
|             <v-textarea | ||||
|                     outlined | ||||
|                     v-model="base64" | ||||
|                     label="Base64 text" | ||||
|                     :rules="rules.base64" | ||||
|                     ref="base64" | ||||
|             ></v-textarea> | ||||
|             <div class="text-center"> | ||||
|                 <v-btn class="mr-1" depressed @click="copy(clear)">Copy clear</v-btn> | ||||
|                 <v-btn class="ml-1" depressed @click="copy(base64)">Copy base64</v-btn> | ||||
|             </div> | ||||
|         </v-card-text> | ||||
|     </v-card> | ||||
|  | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import {copyable} from "../../mixins/copyable.mixin"; | ||||
|  | ||||
|     export default { | ||||
|         name: "StringToBase64", | ||||
|         mixins: [copyable], | ||||
|         data() { | ||||
|             return { | ||||
|                 clear: 'Lorem ipsum dolor sit amet.', | ||||
|                 rules:{ | ||||
|                     base64: [ | ||||
|                         v => { | ||||
|                             try{ | ||||
|                                 return btoa(atob(v)) === v || 'Input is not base64.' | ||||
|                             }catch (e) { | ||||
|                                 return 'Input is not base64.' | ||||
|                             } | ||||
|                         } | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             base64: { | ||||
|                 get(){ | ||||
|                     return btoa(this.clear) | ||||
|                 }, | ||||
|                 set(value){ | ||||
|                     if(this.$refs.base64.validate()){ | ||||
|                         this.clear = atob(value) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
| @@ -3,27 +3,52 @@ | ||||
|         <v-card-title>Uuid v4 generator</v-card-title> | ||||
|  | ||||
|         <v-card-text> | ||||
|             <v-text-field outlined v-model="token" class="centered-input"/> | ||||
|             <v-text-field | ||||
|                     outlined | ||||
|                     v-model.number="quantity" | ||||
|                     ref="quantity" | ||||
|                     type="number" | ||||
|                     label="Quantity" | ||||
|                     dense | ||||
|                     class="quantity" | ||||
|                     :rules="rules.quantity" | ||||
|             /> | ||||
|             <v-textarea outlined v-model="token" class="centered-input" :rows="quantity <= 10 ? quantity : 10" | ||||
|                         readonly/> | ||||
|  | ||||
|             <div class="text-center"> | ||||
|                 <v-btn @click="refreshBool = !refreshBool" depressed class="mr-4">Refresh</v-btn> | ||||
|                 <v-btn @click="copyToken()" depressed>Copy token</v-btn> | ||||
|                 <v-btn @click="copyToken()" depressed>Copy uuid{{ quantity > 1 ? 's' : ''}}</v-btn> | ||||
|             </div> | ||||
|         </v-card-text> | ||||
|     </v-card> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     import {copyToClipboard} from "../../utils/helpers"; | ||||
|     import {copyToClipboard, isInt} from "../../utils/helpers"; | ||||
|  | ||||
|     const noop = () => { | ||||
|     }; | ||||
|  | ||||
|     const generateUuid = () => ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); | ||||
|  | ||||
|     export default { | ||||
|         name: "UuidGenerator", | ||||
|         data: () => ({ | ||||
|             refreshBool: true | ||||
|             refreshBool: true, | ||||
|             quantity: 1, | ||||
|             rules: { | ||||
|                 quantity: [ | ||||
|                     v => !!v || 'Quantity is required', | ||||
|                     v => (v > 0 && v <= 50 ) || 'Quantity should be > 0 and <= 50', | ||||
|                     v => isInt(v) || 'Quantity should be an integer' | ||||
|                 ] | ||||
|             }, | ||||
|             isMounted:false | ||||
|         }), | ||||
|         mounted() { | ||||
|             this.isMounted = true; | ||||
|         }, | ||||
|         methods: { | ||||
|             copyToken() { | ||||
|                 copyToClipboard(this.token); | ||||
| @@ -32,16 +57,32 @@ | ||||
|         }, | ||||
|         computed: { | ||||
|             token() { | ||||
|                 if (this.refreshBool) noop(); // To force recomputation | ||||
|                 if (this.isMounted && this.$refs.quantity.validate()) { | ||||
|                     if (this.refreshBool) noop(); // To force recomputation | ||||
|  | ||||
|                 return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); | ||||
|                     return Array.from({length: this.quantity}, generateUuid).join('\n'); | ||||
|                 } else { | ||||
|                     return ''; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|     ::v-deep .centered-input input { | ||||
|         text-align: center | ||||
| <style scoped lang="less"> | ||||
|     .quantity { | ||||
|         width: 100px; | ||||
|         margin: auto; | ||||
|         text-align: center; | ||||
|  | ||||
|         ::v-deep input { | ||||
|             text-align: center; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ::v-deep .centered-input textarea { | ||||
|         text-align: center; | ||||
|         margin-top: 13px !important; | ||||
|         font-family: Consolas, monospace; | ||||
|     } | ||||
| </style> | ||||
| @@ -25,12 +25,36 @@ const formatBytes = (bytes, decimals = 2) => { | ||||
| } | ||||
|  | ||||
| const isInt = (value) => { | ||||
|     return !isNaN(value) && ((x) => (x | 0) === x)(parseFloat(value)) | ||||
|     return Number.isInteger(value); | ||||
| } | ||||
|  | ||||
| const debounce = (callback, delay = 300) => { | ||||
|     let timer; | ||||
|  | ||||
|     return function(...args) { | ||||
|         clearTimeout(timer); | ||||
|         timer = setTimeout(() => callback(...args), delay); | ||||
|     } | ||||
| } | ||||
|  | ||||
| const randFromArray = (array) => array[Math.floor(Math.random() * array.length)]; | ||||
|  | ||||
| const randIntFromInterval = (min, max) => Math.floor(Math.random() * (max - min) + min) | ||||
|  | ||||
| const downloadBase64File = (dataUrl, name = 'file') => { | ||||
|     const a = document.createElement("a"); | ||||
|     a.href = dataUrl; | ||||
|     a.download = name; | ||||
|     a.click(); | ||||
| } | ||||
|  | ||||
| export { | ||||
|     copyToClipboard, | ||||
|     fileIsImage, | ||||
|     formatBytes, | ||||
|     isInt | ||||
|     isInt, | ||||
|     debounce, | ||||
|     randFromArray, | ||||
|     randIntFromInterval, | ||||
|     downloadBase64File | ||||
| } | ||||
							
								
								
									
										7
									
								
								vercel.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vercel.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "version": 2, | ||||
|   "routes": [ | ||||
|     { "handle": "filesystem" }, | ||||
|     { "src": "/.*", "dest": "/index.html" } | ||||
|   ] | ||||
| } | ||||
| @@ -6,6 +6,11 @@ module.exports = { | ||||
|   ], | ||||
|   configureWebpack: () => { | ||||
|     return { | ||||
|       module:{ | ||||
|         rules: [ | ||||
|           { test: /\.md$/, use: 'raw-loader' } | ||||
|         ] | ||||
|       }, | ||||
|       plugins: [ | ||||
|         new webpack.DefinePlugin({ | ||||
|           'process.env': { | ||||
| @@ -14,5 +19,11 @@ module.exports = { | ||||
|         }) | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   pwa: { | ||||
|     workboxOptions: { | ||||
|       skipWaiting: true, | ||||
|       clientsClaim: true, | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user