Compare commits

...

32 Commits

Author SHA1 Message Date
Corentin Thomasset
44a18a1484 1.0.0 2020-06-07 13:39:15 +02:00
Corentin Thomasset
c6dd9b4ff9 docs: updated README.md
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:40:00 +02:00
Corentin Thomasset
ef2f5bae33 fix: proper checklist
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:36:40 +02:00
Corentin Thomasset
4fdd6439ca docs: updated README.md
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:35:22 +02:00
Corentin Thomasset
e25928e434 refactor: updated banner.png
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:35:06 +02:00
Corentin Thomasset
67808db93a docs: removed previous title
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:19:41 +02:00
Corentin Thomasset
201cf24325 docs: added logo in README.md
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:18:55 +02:00
Corentin Thomasset
caa559e905 chore: added LICENSE
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:18:35 +02:00
Corentin Thomasset
dfc8876e71 chore: added CONTRIBUTING.md
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:18:22 +02:00
Corentin Thomasset
d512db0951 chore: added CODE_OF_CONDUCT.md
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:18:14 +02:00
Corentin Thomasset
8a054561bc feat: dynamic title/description
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 12:05:08 +02:00
Corentin Thomasset
d22988ba4e refactor: indent/format
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 10:22:43 +02:00
Corentin Thomasset
95df202ea1 fix: input date format not updating
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 10:22:12 +02:00
Corentin Thomasset
cab52baa66 refactor: inputs length
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-07 10:21:53 +02:00
Corentin Thomasset
f2af2ed35d feat: abstract
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-06 23:41:09 +02:00
Corentin Thomasset
7dc70c5741 refactor: cleaned ColorConverter
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-06 15:00:54 +02:00
Corentin Thomasset
db189b4bcb refactor: using "color-convert" in ColorConverter
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-06 13:51:39 +02:00
Corentin Thomasset
d4f57fde34 feat: ColorConverter
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-04 15:01:16 +02:00
Corentin Thomasset
da2891751d feat: ColorConverter
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-04 15:01:03 +02:00
Corentin Thomasset
793d840c49 refactor: font awesome 5 icons
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-04 13:30:27 +02:00
Corentin Thomasset
4801e2f6c5 feat: home page
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-03 13:46:15 +02:00
Corentin Thomasset
e47ff2456b feat: UuidGenerator
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-01 22:16:23 +02:00
Corentin Thomasset
0c120a3c5a feat: BaseConverter
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-01 21:40:35 +02:00
Corentin Thomasset
e5969a534f feat: TextStats + better searchbar
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-06-01 18:27:49 +02:00
Corentin Thomasset
abc7fc6259 feat: TextCypher
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-05-31 23:33:19 +02:00
Corentin Thomasset
1876db0ddc feat: FileUploader + Base64
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-05-31 22:31:14 +02:00
Corentin Thomasset
da092a9bd3 feat: searchbar in menu on small screens
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
2020-05-31 22:09:20 +02:00
CorentinTh
49f0721107 feat: search bar 2020-05-15 12:27:18 +02:00
CorentinTh
34889eb1ce feat: urlencoder + file in base64 2020-04-27 00:39:40 +02:00
CorentinTh
3ef90b84ca feat: improved date converter input 2020-04-26 16:02:39 +02:00
CorentinTh
662b84cda0 feat: DateConverter
Signed-off-by: CorentinTh <corentin.thomasset74@gmail.com>
2020-04-25 21:05:40 +02:00
CorentinTh
e1ebeefdc4 feat: TokenGenerator + Hash 2020-04-25 18:43:17 +02:00
38 changed files with 2435 additions and 340 deletions

BIN
.github/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at contact@corentin-thomasset.fr. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

92
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,92 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at contact@corentin-thomasset.fr. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Corentin THOMASSET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,24 +1,53 @@
# it-tools
![logo](.github/logo.png)
Aggregated set of useful tools that every developer may need once in a while. Available [here](https://it-tools.tech).
## Functionality/roadmap
Here is an unordered list of the current functionalities, and some that may come.
- [x] Token generator
- [x] Uuid generator
- [x] String hash
- [x] Text encryption
- [x] Date format converter
- [x] Int base converter
- [x] Color format converter
- [x] Url encoder
- [x] Base 64 generator
- [x] Text information
- [ ] Lorem ipsum text generator
- [ ] Image exif editor/remover
- [ ] QR code generator
- [ ] Bip39 pass-phrase generator
- [ ] Crontab friendly generator
- [ ] Image format converter?
- [ ] Image cropper
You have an idea of a tool? Submit a feature request!
## Project setup
```
Install dependencies by running the following command:
```shell
npm install
```
### Compiles and hot-reloads for development
```
Then compiles and hot-reloads for development:
```shell
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
And to lint and fixe files, run:
```shell
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## Contribute
**Pull requests are welcome !** Feel free to contribute.
## 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).

536
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "it-tools",
"version": "0.1.0",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -972,6 +972,12 @@
"to-fast-properties": "^2.0.0"
}
},
"@fortawesome/fontawesome-free": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz",
"integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==",
"dev": true
},
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@@ -1987,6 +1993,25 @@
"dev": true,
"requires": {
"color-convert": "^1.9.0"
},
"dependencies": {
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
},
"dependencies": {
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
}
}
}
}
},
"any-observable": {
@@ -2220,6 +2245,37 @@
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
"dev": true
},
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"requires": {
"follow-redirects": "1.5.10"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"babel-eslint": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -3195,6 +3251,17 @@
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
"clone-deep": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
"integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
"dev": true,
"requires": {
"is-plain-object": "^2.0.4",
"kind-of": "^6.0.2",
"shallow-clone": "^3.0.0"
}
},
"coa": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
@@ -3230,22 +3297,46 @@
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
},
"dependencies": {
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
},
"dependencies": {
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
}
}
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "1.1.3"
"color-name": "~1.1.4"
},
"dependencies": {
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"color-string": {
"version": "1.5.3",
@@ -3640,6 +3731,11 @@
"randomfill": "^1.0.3"
}
},
"crypto-js": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz",
"integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q=="
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@@ -4751,9 +4847,9 @@
"dev": true
},
"eventemitter3": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
"integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
"dev": true
},
"events": {
@@ -6146,6 +6242,11 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"headful": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/headful/-/headful-1.0.3.tgz",
"integrity": "sha512-vF9Vfddn1QWmziliht2mji6ayI78+hUuSC+Kt0GEqLw/51zWgi1KF7oLtIQf3nlkg8sQQOlznkkIaF4W9lIt9w=="
},
"hex-color-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
@@ -6212,9 +6313,9 @@
"dev": true
},
"html-entities": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
"integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
"dev": true
},
"html-minifier": {
@@ -6363,16 +6464,10 @@
}
}
},
"http-parser-js": {
"version": "0.4.10",
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
"dev": true
},
"http-proxy": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
"integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
@@ -6665,6 +6760,12 @@
}
}
},
"interpret": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
"dev": true
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -6674,12 +6775,6 @@
"loose-envify": "^1.0.0"
}
},
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -7213,15 +7308,6 @@
"launch-editor": "^2.2.1"
}
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
"invert-kv": "^2.0.0"
}
},
"less": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/less/-/less-3.11.1.tgz",
@@ -7963,9 +8049,9 @@
}
},
"loglevel": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz",
"integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==",
"version": "1.6.8",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
"integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
"dev": true
},
"loose-envify": {
@@ -8002,15 +8088,6 @@
"semver": "^5.6.0"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -8049,25 +8126,6 @@
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
"mem": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
"integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
},
"dependencies": {
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
}
}
},
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -8623,10 +8681,14 @@
"dev": true
},
"object-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
"integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
"dev": true
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
"integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"object-keys": {
"version": "1.1.1",
@@ -8803,41 +8865,18 @@
"integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
"dev": true
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
"integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
"dev": true
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
"p-is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
"integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
"dev": true
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -10050,6 +10089,15 @@
"readable-stream": "^2.0.2"
}
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"dev": true,
"requires": {
"resolve": "^1.1.6"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@@ -10386,6 +10434,11 @@
"inherits": "^2.0.1"
}
},
"roboto-fontface": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
},
"run-async": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
@@ -10440,6 +10493,36 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sass": {
"version": "1.26.3",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz",
"integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
}
},
"sass-loader": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz",
"integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==",
"dev": true,
"requires": {
"clone-deep": "^4.0.1",
"loader-utils": "^1.2.3",
"neo-async": "^2.6.1",
"schema-utils": "^2.6.1",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -10660,6 +10743,15 @@
"safe-buffer": "^5.0.1"
}
},
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
"integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
"dev": true,
"requires": {
"kind-of": "^6.0.2"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -10681,6 +10773,17 @@
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
"dev": true
},
"shelljs": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
"integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
"dev": true,
"requires": {
"glob": "^7.0.0",
"interpret": "^1.0.0",
"rechoir": "^0.6.2"
}
},
"signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
@@ -10852,13 +10955,14 @@
}
},
"sockjs": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
"integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
"integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==",
"dev": true,
"requires": {
"faye-websocket": "^0.10.0",
"uuid": "^3.0.1"
"uuid": "^3.4.0",
"websocket-driver": "0.6.5"
}
},
"sockjs-client": {
@@ -12013,6 +12117,32 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
},
"vue-cli-plugin-vuetify": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.5.tgz",
"integrity": "sha512-jtxcidjLT5f1H9QLYKLFjo/ZG42ud4pI9bK3WNO5DXyhiMDMTwSZ7b3NcJVRH7tKYbv5/ty0VPdDGlf4w22AMA==",
"dev": true,
"requires": {
"semver": "^7.1.2",
"shelljs": "^0.8.3"
},
"dependencies": {
"semver": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.2.1.tgz",
"integrity": "sha512-aHhm1pD02jXXkyIpq25qBZjr3CQgg8KST8uX0OWXch3xE6jw+1bfbWnCjzMwojsTquroUmKFHNzU6x26mEiRxw==",
"dev": true
}
}
},
"vue-cryptojs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/vue-cryptojs/-/vue-cryptojs-2.1.4.tgz",
"integrity": "sha512-AmJ5HAbvW81X4DLzq5Q3YlFbFIr1vSAmjXcr5SxyJCJeoH/2R+HHuU6D21pI2pAv4LCqfRpakWMWs6Cl8XvtAA==",
"requires": {
"crypto-js": "^3.3.0"
}
},
"vue-eslint-parser": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz",
@@ -12039,6 +12169,14 @@
}
}
},
"vue-headful": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/vue-headful/-/vue-headful-2.1.0.tgz",
"integrity": "sha512-HXMdJfVDgD3eoh8qk9Laz2wGraPVGYDbvfXd7+i1MVziWMXGHUm8gpRC2p11ugYYXKRAcaS2g6ycq4Pr+eqb7g==",
"requires": {
"headful": "^1.0.3"
}
},
"vue-hot-reload-api": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -12105,6 +12243,25 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vuetify": {
"version": "2.2.20",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.2.20.tgz",
"integrity": "sha512-tiij3ammBiG7QrdwRBJxW917AyDLcMekRtLosdyVshDKHA5CpKrru8Ljn1gAmeUyhYZyUw4gz3XbrrIJ++TP7w=="
},
"vuetify-loader": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/vuetify-loader/-/vuetify-loader-1.4.3.tgz",
"integrity": "sha512-fS0wRil682Ebsj2as+eruBoMPKaQYDhu/fDAndnTItzSY4RK4LOEIsssVL4vD6QY8dvUgoGL84SUQ6vGr777CA==",
"dev": true,
"requires": {
"loader-utils": "^1.2.0"
}
},
"vuetify-toast-snackbar": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/vuetify-toast-snackbar/-/vuetify-toast-snackbar-0.6.1.tgz",
"integrity": "sha512-F2bLPMXiw7qQgX68adSu0zQrMTKODN5JAzCP8AP/HGhz/Bz1z24QbvYcX0fyHnG/yP6PAPkpMCXsLQSq6HV0Ag=="
},
"watchpack": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
@@ -12231,9 +12388,9 @@
}
},
"webpack-dev-server": {
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz",
"integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==",
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz",
"integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
@@ -12244,31 +12401,31 @@
"debug": "^4.1.1",
"del": "^4.1.1",
"express": "^4.17.1",
"html-entities": "^1.2.1",
"html-entities": "^1.3.1",
"http-proxy-middleware": "0.19.1",
"import-local": "^2.0.0",
"internal-ip": "^4.3.0",
"ip": "^1.1.5",
"is-absolute-url": "^3.0.3",
"killable": "^1.0.1",
"loglevel": "^1.6.6",
"loglevel": "^1.6.8",
"opn": "^5.5.0",
"p-retry": "^3.0.1",
"portfinder": "^1.0.25",
"portfinder": "^1.0.26",
"schema-utils": "^1.0.0",
"selfsigned": "^1.10.7",
"semver": "^6.3.0",
"serve-index": "^1.9.1",
"sockjs": "0.3.19",
"sockjs": "0.3.20",
"sockjs-client": "1.4.0",
"spdy": "^4.0.1",
"spdy": "^4.0.2",
"strip-ansi": "^3.0.1",
"supports-color": "^6.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "^3.7.2",
"webpack-log": "^2.0.0",
"ws": "^6.2.1",
"yargs": "12.0.5"
"yargs": "^13.3.2"
},
"dependencies": {
"ansi-regex": {
@@ -12278,33 +12435,39 @@
"dev": true
},
"cliui": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
"ansi-regex": "^4.1.0"
}
}
}
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
@@ -12314,12 +12477,6 @@
"locate-path": "^3.0.0"
}
},
"get-caller-file": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true
},
"is-absolute-url": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
@@ -12366,11 +12523,27 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true
"portfinder": {
"version": "1.0.26",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
"integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==",
"dev": true,
"requires": {
"async": "^2.6.2",
"debug": "^3.1.1",
"mkdirp": "^0.5.1"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"schema-utils": {
"version": "1.0.0",
@@ -12390,28 +12563,29 @@
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
"strip-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
"ansi-regex": "^4.1.0"
}
}
}
@@ -12435,61 +12609,55 @@
}
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
},
"dependencies": {
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
"ansi-regex": "^4.1.0"
}
}
}
},
"yargs": {
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true,
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.2.0",
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
"os-locale": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1 || ^4.0.0",
"yargs-parser": "^11.1.1"
"y18n": "^4.0.0",
"yargs-parser": "^13.1.2"
}
},
"yargs-parser": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
@@ -12536,13 +12704,11 @@
}
},
"websocket-driver": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
"integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
"integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=",
"dev": true,
"requires": {
"http-parser-js": ">=0.4.0 <0.4.11",
"safe-buffer": ">=5.1.0",
"websocket-extensions": ">=0.1.1"
}
},

View File

@@ -1,6 +1,7 @@
{
"name": "it-tools",
"version": "0.1.0",
"description": "",
"version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -8,12 +9,21 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.19.2",
"color-convert": "^2.0.1",
"color-name": "^1.1.4",
"core-js": "^3.6.4",
"register-service-worker": "^1.7.1",
"roboto-fontface": "*",
"vue": "^2.6.11",
"vue-router": "^3.1.6"
"vue-cryptojs": "^2.1.4",
"vue-headful": "^2.1.0",
"vue-router": "^3.1.6",
"vuetify": "^2.2.11",
"vuetify-toast-snackbar": "^0.6.1"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.13.0",
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-plugin-pwa": "~4.3.0",
@@ -25,7 +35,11 @@
"less": "^3.0.4",
"less-loader": "^5.0.0",
"lint-staged": "^9.5.0",
"vue-template-compiler": "^2.6.11"
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"vue-cli-plugin-vuetify": "~2.0.5",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.3.0"
},
"gitHooks": {
"pre-commit": "lint-staged"

BIN
public/img/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -6,6 +6,16 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<meta itemprop="name">
<meta property="og:title">
<meta name="twitter:title">
<meta name="description"/>
<meta itemprop="description">
<meta property="og:description">
<meta name="twitter:description">
<meta itemprop="image">
<meta property="og:image">
<meta name="twitter:image">
</head>
<body>
<noscript>

View File

@@ -1,32 +1,195 @@
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</div>
<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"
/>
<v-navigation-drawer v-model="drawer" app clipped>
<SearchBar class="hidden-sm-and-up" />
<v-list dense>
<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-list-item-action>
<v-list-item-content>
<v-list-item-title>
{{ item.text }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</div>
</v-list>
<template v-slot:append>
<v-divider></v-divider>
<div class="pa-5">
<div>
IT-Tools <a v-bind:href="'https://github.com/CorentinTh/it-tools/tree/'+appVersion"
target="_blank">{{appVersion}}</a>
</div>
<div>&copy; {{new Date().getFullYear()}} <a href="//corentin-thomasset.fr" class="footer-link">Corentin
Thomasset</a></div>
</div>
</template>
</v-navigation-drawer>
<v-app-bar app clipped-left color="green">
<v-app-bar-nav-icon @click.stop="drawer = !drawer"/>
<v-toolbar-title class="mr-12 align-center">
<router-link to="/" class="title">IT Tools</router-link>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-row
align="center"
style="max-width: 650px"
class="hidden-sm-and-down"
>
<SearchBar/>
</v-row>
<v-spacer></v-spacer>
<div class="right-links">
<router-link to="/about">About</router-link>
<a href="https://github.com/CorentinTh/it-tools" target="_blank" class="navbar-link">
<v-icon>fab fa-github</v-icon>
</a>
</div>
</v-app-bar>
<v-content>
<v-row class="fill-height pa-4" justify="center" align="center" no-gutters>
<router-view></router-view>
</v-row>
</v-content>
<!-- <v-footer app>-->
<!-- <span>&copy; {{new Date().getFullYear()}} <a href="//corentin-thomasset.fr" class="footer-link">Corentin Thomasset</a></span>-->
<!-- <span>A bug ? A feature request ? Stuff happens <a href="https://github.com/CorentinTh/it-tools/issues"-->
<!-- target="_blank" class="footer-link">here</a>.</span>-->
<!-- </v-footer>-->
</v-app>
</template>
<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
<script>
import SearchBar from "./components/SearchBar";
import {toolsComponents} from "./router";
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
export default {
props: {
source: String,
},
components: {SearchBar},
data: () => ({
appVersion: 'v' + process.env.APPLICATION_VERSION,
drawer: null,
items: toolsComponents,
currentRoute:{}
}),
mounted() {
this.setTitle()
},
created() {
this.$vuetify.theme.dark = true
},
methods:{
setTitle(){
const path = this.$router.currentRoute.path;
this.currentRoute = toolsComponents.map(p => p.child).flat().find(p => p.path === path)
}
},
watch:{
'$route'(){
this.setTitle()
}
}
}
}
}
</style>
</script>
<style lang="less">
html {
overflow-y: auto !important;
}
.single-card {
width: 100%;
max-width: 700px !important;
}
.v-card__title {
justify-content: center;
font-size: 30px !important;
line-height: 30px !important;
padding: 30px 0 !important;
font-weight: 300 !important;
}
.title {
text-decoration: none;
color: #fff !important;
&:hover {
border-bottom: 1px dashed;
}
}
.right-links {
align-items: center;
display: flex;
a:not(:last-child) {
margin-right: 20px;
text-decoration: none;
color: #fff;
&:hover {
border-bottom: 1px dashed;
}
}
.navbar-link {
text-decoration: none;
.v-icon {
font-size: 37px !important;
&:hover {
color: #363636;
}
}
}
}
.v-navigation-drawer__append {
text-align: center;
color: rgba(255, 255, 255, 0.52) !important;
a {
border-bottom: 1px dashed;
text-decoration: none;
color: rgba(255, 255, 255, 0.52) !important;
&:hover {
color: #4CAF50 !important;
border-bottom: 1px solid;
}
}
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,48 @@
<template>
<div>
<h2>Hello, World!</h2>
<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.
</p>
<v-row>
<v-col cols="12" lg="6" md="12">
<h2>A tool is missing?</h2>
<p class="text-justify">
If you need a tool that is not currently not present here, and you think can be
relevant, you are welcome to submit a feature request <a
href="//github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D%20My%20feature">here</a>.
</p>
</v-col>
<v-col cols="12" lg="6" md="12">
<h2>Found a bug?</h2>
<p class="text-justify">
If you found a bug, or something broken that doesn't work as expected, please fill a bug report here <a
href="//github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=bug&template=bug_report.md&title=%5BBUG%5D%20My%20bug">here</a>.
</p>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
name: "Abstract"
}
</script>
<style scoped lang="less">
a {
border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
text-decoration: none;
color: inherit !important;
&:hover {
color: #4CAF50 !important;
border-bottom: 1px solid;
}
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<div
class="drop-area pa-4 text-center"
:class="{'drag-over':dragging, 'pb-0':!loading}"
@dragover.prevent
@drop.prevent="imageDropped"
@dragenter="dragEnter()"
@dragend="dragEnd()"
@dragleave="dragLeave()"
@dragexit="dragExit()"
>
<div v-if="loading">
<v-progress-circular
indeterminate
color="primary"
/>
</div>
<div v-else>
<p>Drag & drop a file here</p>
<p class="or">or</p>
<v-btn depressed @click="manualUploadClicked">select a file</v-btn>
<input ref="uploadInput" type="file" hidden @change="(e) => handleFiles(e.target.files[0])">
<div v-if="allowUrl">
<p class="or">or</p>
<v-text-field
ref="urlInput"
@click:append="urlFilled(url)"
@keypress.enter="urlFilled(url)"
v-model="url"
append-icon="fa-arrow-right"
dense
label="Paste the file url"
outlined
:error-messages="urlErrors"
/>
</div>
</div>
</div>
</template>
<script>
import * as axios from "axios";
export default {
name: "FileUploader",
props: {
allowUrl: {
type: Boolean,
default: true
}
},
data() {
return {
dragging: false,
urlErrors: undefined,
dragEnterCounter: 0,
url: '',
loading: false
}
},
methods: {
imageDropped(e) {
this.dragging = false;
if (e.dataTransfer.items.length > 0) {
const item = e.dataTransfer.items[0];
switch (item.kind) {
case 'string':
item.getAsString(url => this.urlFilled(url));
break;
case 'file':
this.handleFiles(item.getAsFile());
break;
}
}
},
dragEnter() {
this.dragEnterCounter++;
this.dragging = true;
},
dragLeave() {
if (--this.dragEnterCounter <= 0) {
this.dragging = false;
}
},
async urlFilled(url) {
if (url && url.length > 0) {
this.loading = true;
try {
const {data, headers} = await axios.get(url);
const name = url.split('/').pop();
const file = new File([data], name, {type: headers['content-type']})
this.handleFiles(file);
} catch (ignored) {
this.urlErrors = 'Incorrect url'
}
this.loading = false;
}
},
dragEnd() {
this.dragging = false;
},
dragExit() {
this.dragging = false;
},
handleFiles(file) {
if (!file) return;
this.$emit('input', file)
},
manualUploadClicked() {
this.$refs.uploadInput.click()
}
}
}
</script>
<style lang="less">
.drop-area {
border: 2px dashed #363636;
border-radius: 10px;
& > *, .v-btn {
margin: 0 !important;
}
.or {
opacity: 0.7;
margin: 5px 0 !important;
}
&.drag-over {
border-color: #4CAF50;
}
.v-input__icon {
button {
margin-top: 0 !important;
}
}
}
</style>

View File

@@ -1,60 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa" target="_blank" rel="noopener">pwa</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<v-autocomplete
label="Search..."
single-line
append-icon="fa-search"
color="white"
hide-details
:items="items"
item-text="text"
item-value="path"
solo-inverted
@change="choose"
:filter="filter"
clearable
cache-items
>
<template v-slot:no-data>
<v-list-item>
<v-list-item-title>
Search for the <strong>tool</strong> you need!
</v-list-item-title>
</v-list-item>
</template>
</v-autocomplete>
</template>
<script>
import {toolsComponentsFlat} from '../router'
export default {
name: "SearchBar",
data() {
const vm = this;
return {
items: toolsComponentsFlat,
choose(path) {
vm.$router.push(path).catch(() => {
})
}
}
},
methods: {
filter(item, queryText, itemText) {
const query = queryText.trim().toLowerCase();
const nameContainsText = itemText.toLowerCase().includes(query);
const keywordContainsText = item.keywords ? item.keywords.some(keyword => keyword.toLowerCase().includes(query)) : false;
return nameContainsText || keywordContainsText;
}
}
}
</script>
<style scoped lang="less">
::v-deep .v-list-item__mask{
color: inherit !important;
background: inherit !important;
}
</style>

View File

@@ -2,10 +2,17 @@ import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import vuetify from './plugins/vuetify'
import 'roboto-fontface/css/roboto/roboto-fontface.css'
import '@fortawesome/fontawesome-free/css/all.css'
import './plugins/crypto-js'
import './plugins/toast-snackbar'
import './plugins/vue-headful'
Vue.config.productionTip = false
new Vue({
router,
vuetify,
render: h => h(App)
}).$mount('#app')

4
src/plugins/crypto-js.js Normal file
View File

@@ -0,0 +1,4 @@
import Vue from 'vue'
import VueCryptojs from 'vue-cryptojs'
Vue.use(VueCryptojs)

View File

@@ -0,0 +1,4 @@
import Vue from 'vue'
import VuetifyToast from 'vuetify-toast-snackbar'
Vue.use(VuetifyToast)

View File

@@ -0,0 +1,4 @@
import Vue from 'vue';
import vueHeadful from 'vue-headful';
Vue.component('vue-headful', vueHeadful);

29
src/plugins/vuetify.js Normal file
View File

@@ -0,0 +1,29 @@
import Vue from 'vue';
import Vuetify, { VSnackbar, VBtn, VIcon } from 'vuetify/lib'
Vue.use(Vuetify, {
components: {
VSnackbar,
VBtn,
VIcon
}
})
export default new Vuetify({
theme: {
themes: {
theme: 'dark',
dark: {
primary: '#4CAF50',
secondary: '#424242',
accent: '#4CAF50',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107'
},
},
},
icons: {
iconfont: 'fa',
},
});

138
src/router.js Normal file
View File

@@ -0,0 +1,138 @@
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',
child: [
{
icon: 'fa-key',
text: 'Token generator',
path: '/token-generator',
component: TokenGenerator,
keywords: ['token', 'random', 'string', 'alphanumeric'],
description: 'Generate random tokens.'
},
{
icon: 'fa-fingerprint',
text: 'Uuid generator',
path: '/uuid-generator',
component: UuidGenerator,
keywords: ['token', 'v4', 'string', 'alphanumeric']
},
{
icon: 'fa-font',
text: 'Hash text',
path: '/hash',
component: Hash,
keywords: ['md5', 'sha1', 'sha256', 'sha224', 'sha512', 'sha384', 'sha3', 'ripemd160', 'random']
},
{
icon: 'fa-lock',
text: 'Cypher/uncypher text',
path: '/cypher',
component: TextCypher,
keywords: ['aes', 'tripledes', 'rabbit', 'rabbitlegacy', 'rc4']
},
],
},
{
title: 'Converter',
child: [
{
icon: 'fa-calendar',
text: 'Date/Time converter',
path: '/date-converter',
component: 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,
keywords: ['binary', 'hexadecimal', 'decimal']
},
{
icon: 'fa-palette',
text: 'Color picker/converter',
path: '/color-picker-converter',
component: ColorConverter,
keywords: ['rgb', 'rgba', 'hexadecimal', 'hsla', 'red', 'green', 'blue', 'alpha']
},
],
},
{
title: 'Web',
child: [
{
icon: 'fa-link',
text: 'URL encode/decode',
path: '/url-encoder',
component: UrlEncoder,
keywords: ['%20']
},
{
icon: 'fa-file-export',
text: 'File to Base64',
path: '/file-to-base64',
component: FileToBase64
},
],
},
{
title: 'Miscellaneous',
child: [
{
icon: 'fa-align-left\n',
text: 'Text stats',
path: '/text-stats',
component: TextStats,
keywords: ['word', 'count', 'size', 'bytes', 'length']
},
],
}
];
const toolsComponentsFlat = toolsComponents.reduce((acc, section) => [...acc, ...section.child], [])
const routes = [
...toolsComponentsFlat,
{
path: '/',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('./routes/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
export default router;
export {
routes,
toolsComponents,
toolsComponentsFlat
};

View File

@@ -1,29 +0,0 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router

19
src/routes/About.vue Normal file
View File

@@ -0,0 +1,19 @@
<template>
<v-card class="single-card">
<v-card-title>About</v-card-title>
<v-card-text>
<Abstract />
</v-card-text>
</v-card>
</template>
<script>
import Abstract from "../components/Abstract";
export default {
name: "About",
components : {
Abstract
},
}
</script>

118
src/routes/Home.vue Normal file
View File

@@ -0,0 +1,118 @@
<template>
<div>
<v-row justify="center" align="center">
<v-col cols="12" lg="5" md="12">
<v-card>
<v-card-title>IT-Tools</v-card-title>
<v-card-text class="description">
<Abstract />
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row justify="center" align="center">
<v-col cols="12" lg="8" md="12">
<v-card class="card-auto">
<v-card-text>
<div class="card-wrapper ">
<div v-for="item in items" :key="item.text" class="pa-2">
<v-card class="card-element" color="primary" :to="item.path">
<v-card-text>
<v-icon>{{ item.icon }}</v-icon>
<div class="item-title">{{item.text}}</div>
</v-card-text>
</v-card>
</div>
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
import {toolsComponentsFlat} from "../router";
import Abstract from "../components/Abstract";
export default {
name: 'Home',
components : {
Abstract
},
data: () => ({
items: toolsComponentsFlat
})
}
</script>
<style lang="less" scoped>
.card-auto {
width: auto;
}
.card-wrapper {
display: flex;
flex-wrap: wrap;
div {
flex: 0 1 20%;
@media only screen and (max-width: 800px) {
flex: 0 1 33%;
}
@media only screen and (max-width: 620px) {
flex: 0 1 50%;
}
@media only screen and (max-width: 420px) {
flex: 0 1 220px;
margin: auto;
}
@media only screen and (max-width: 300px) {
flex: 0 1 100%;
}
.card-element {
width: 100%;
border-radius: 24px 4px !important;
color: #ffffff !important;
font-weight: bold !important;
text-align: center;
transition: 0.2s ease;
position: relative;
top: 0;
&:hover {
position: relative;
top: -5px;
opacity: 0.9;
}
.v-card__text {
padding: 16px 10px;
}
.v-icon {
font-size: 40px;
color: #ffffff;
margin: 10px 0;
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
.item-title {
overflow-wrap: anywhere;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
line-height: 22px;
height: 44px;
opacity: 1;
color: #ffffff;
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
}
}
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<v-card class="single-card">
<v-card-title>Base converter</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" sm="4">
<v-text-field
label="Input base"
outlined
type="number"
v-model="inputBase"
hide-details="auto"
:rules="baseRules"
/>
</v-col>
<v-col cols="12" sm="8">
<v-text-field
ref="inputField"
label="Input number"
outlined
v-model="inputNumber"
hide-details="auto"
/>
</v-col>
</v-row>
<br>
<v-divider/>
<br>
<v-row>
<v-col cols="12" sm="4">
<v-text-field
label="Output base"
outlined
type="number"
v-model="outputBase"
:rules="baseRules"
/>
</v-col>
<v-col cols="12" sm="8">
<v-text-field
label="Output number"
outlined
v-model="outputNumber"
readonly
append-icon="fa-clipboard"
@click:append="copy"
/>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
<script>
import {copyToClipboard} from "../../utils/helpers";
const convertBase = (value, fromBase, toBase) => {
const range = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'.split('');
const fromRange = range.slice(0, fromBase);
const toRange = range.slice(0, toBase);
let decValue = value.split('').reverse().reduce((carry, digit, index) => {
if (fromRange.indexOf(digit) === -1) throw new Error('Invalid digit `' + digit + '` for base ' + fromBase + '.');
return carry += fromRange.indexOf(digit) * (Math.pow(fromBase, index));
}, 0);
let newValue = '';
while (decValue > 0) {
newValue = toRange[decValue % toBase] + newValue;
decValue = (decValue - (decValue % toBase)) / toBase;
}
return newValue || '0';
}
export default {
name: "BaseConverter",
data() {
return {
inputError:'',
inputNumber: '42',
inputBase: 10,
outputBase: 16,
baseRules: [
v => !!v || 'Required',
v => v > 1 || 'Base should be > 1',
v => v <= 64 || 'Base should be <= 64',
],
}
},
methods: {
copy() {
copyToClipboard(this.outputNumber);
this.$toast.success('Copied to clipboard.')
}
},
computed: {
outputNumber() {
try{
return convertBase(this.inputNumber, this.inputBase, this.outputBase)
}catch (e) {
return e.message;
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,218 @@
<template>
<v-card class="single-card">
<v-card-title>Color picker/converter</v-card-title>
<v-card-text>
<v-row no-gutters align="center" align-content="center" justify="center">
<v-col cols="12" sm="6" align="center">
<v-color-picker
flat
canvas-height="300"
hide-inputs
mode="rgba"
v-model="rgbPicker"
/>
</v-col>
<v-col cols="12" sm="6" align="center">
<v-text-field
outlined
ref="hex"
label="hex"
:value="hex"
@input="(v) => updateColors(v, 'hex')"
:rules="rules.hex"
dense
/>
<v-text-field
outlined
label="rgb"
ref="rgb"
:value="rgb"
@input="(v) => updateColors(v, 'rgb')"
:rules="rules.rgb"
dense
/>
<v-text-field
outlined
label="hsl"
ref="hsl"
:value="hsl"
:rules="rules.hsl"
@input="(v) => updateColors(v, 'hsl')"
dense
/>
<v-combobox
:value="keyword"
outlined
label="css keyword"
ref="keyword"
:items="colorsName"
:rules="rules.keyword"
@change="(v) => updateColors(v, 'keyword')"
no-data-text="This is not an authorized color name."
dense
/>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
<script>
import convert from "color-convert";
import colors from "color-name";
const required = v => !!v || 'A value is required'
export default {
name: "ColorConverter",
data: () => ({
rgbPicker: {
"r": 76,
"g": 175,
"b": 80
},
colorsName: Object.keys(colors).sort(),
valid: true,
rules: {
hex: [
required,
v => /^#(?:[0-9a-fA-F]{6})$/.test(v) || 'Format should be like #112233'
],
rgb: [
required,
v => /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.test(v) || 'Format should be like rgb(255, 0, 0)'
],
hsl: [
required,
v => /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/.test(v) || 'Format should be like hsl(360, 100%, 50%)'
],
keywords: [
required,
v => !!colors[v] || 'Value should be from the list'
]
},
hex: '#4CAF50',
rgb: undefined,
hsl: undefined,
keyword: undefined,
}),
mounted() {
this.updateColors(this.hex, 'hex');
},
methods: {
setHSL(r, g, b) {
const [h, s, l] = convert.rgb.hsl(r, g, b)
this.hsl = `hsl(${Math.floor(h)}, ${Math.floor(s)}%, ${Math.floor(l)}%)`;
},
setRGB(r, g, b) {
this.rgb = `rgb(${r}, ${g}, ${b})`;
},
setHEX(r, g, b) {
const result = convert.rgb.hex(r, g, b);
this.hex = `#${result}`
},
setKeyword(r, g, b) {
this.keyword = convert.rgb.keyword(r, g, b);
},
updateColors(value, fromType) {
if (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}
this.setHEX(r, g, b);
this.setHSL(r, g, b);
this.setKeyword(r, g, b);
} else if (fromType === 'hex') {
const [r, g, b] = convert.hex.rgb(value.replace(/#/g, ''));
this.rgbPicker = {r, g, b}
this.setRGB(r, g, b);
this.setHSL(r, g, b);
this.setKeyword(r, g, b);
} else if (fromType === 'hsl') {
const [h, s, l] = value.match(/^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/).slice(1).map(v => parseInt(v));
const [r, g, b] = convert.hsl.rgb(h, s, l)
this.rgbPicker = {r, g, b}
this.setRGB(r, g, b);
this.setHEX(r, g, b);
this.setKeyword(r, g, b);
} else if (fromType === 'keyword') {
try {
const [r, g, b] = convert.keyword.rgb(value);
this.rgbPicker = {r, g, b}
this.setRGB(r, g, b);
this.setHEX(r, g, b);
this.setHSL(r, g, b);
} catch (ignored) {
// ignored
}
}
}
}
},
// computed: {
// _hex: {
// get() {
// const result = convert.rgb.hex(this.rgb.r, this.rgb.g, this.rgb.b)
// return `#${result}`
// },
// set(value) {
// if (this.$refs.hex.validate()) {
// const [r, g, b] = convert.hex.rgb(value.replace(/#/g, ''))
// this.rgb = {r, g, b}
// }
// }
// },
// _rgb: {
// get() {
// return `rgb(${this.rgb.r}, ${this.rgb.g}, ${this.rgb.b})`
// },
// set(value) {
// if (this.$refs.rgb.validate()) {
// const [r, g, b] = value.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(v => parseInt(v));
// this.rgb = {r, g, b}
// }
// }
// },
// _hsl: {
// get() {
// const [h, s, l] = convert.rgb.hsl(this.rgb.r, this.rgb.g, this.rgb.b)
// return `hsl(${Math.floor(h)}, ${Math.floor(s)}%, ${Math.floor(l)}%)`
// },
// set(value) {
// if (this.$refs.hsl.validate()) {
// const [h, s, l] = value.match(/^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/).slice(1).map(v => parseInt(v));
// const [r, g, b] = convert.hsl.rgb(h, s, l)
//
// this.rgb = {r, g, b}
// }
// }
// },
// _keyword: {
// get() {
// return convert.rgb.keyword(this.rgb.r, this.rgb.g, this.rgb.b)
// },
// set(value) {
// if (this.$refs.keyword.validate()) {
// try {
// const [r, g, b] = convert.keyword.rgb(value)
// this.rgb = {r, g, b}
// } catch (ignored) {
// // ignored
// }
// }
// }
// }
// }
}
</script>
<style scoped lang="less">
::v-deep .v-input__icon {
height: 18px !important;
}
</style>

View File

@@ -0,0 +1,157 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" xl="4" lg="6" md="12">
<v-card class="mb-5">
<v-card-title>Input</v-card-title>
<v-card-text>
<div class="text-center">
<v-switch v-model="useCurrentDate" label="Use current date"/>
</div>
<v-divider></v-divider>
<br>
<v-row>
<v-col md="4" sm="12" class="pt-0 pb-0">
<v-select
:items="formats.filter(f => !f.title.toLowerCase().includes('locale')).map(v => v.title)"
outlined
label="Your date format"
placeholder="Input format"
v-model="inputFormatterTitle"
@input="userInputChanged()"
:disabled="useCurrentDate"
/>
</v-col>
<v-col md="8" sm="12" class="pt-0 pb-0">
<v-text-field
outlined
v-model="inputString"
label="Your date string"
@input="userInputChanged()"
:error="invalidInput"
:disabled="useCurrentDate"
/>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" xl="4" lg="6" md="12">
<v-card>
<v-card-title>Dates formats</v-card-title>
<v-card-text>
<v-text-field
dense
readonly
outlined
v-for="format of formats"
v-bind:key="format.title"
:label="format.title"
:value="format.getDate()"
append-icon="fa-clipboard"
@click:append="copyDate(format.getDate())"
/>
</v-card-text>
</v-card>
</v-col>
</v-row>
</template>
<script>
import {copyToClipboard} from "../../utils/helpers";
export default {
name: "DateConverter",
created() {
setInterval(this.refreshCurrentDate.bind(this), 1000);
this.inputFormatterTitle = this.formats[1].title;
},
data() {
const vm = this;
return {
inputString: '',
inputFormatterTitle: undefined,
useCurrentDate: true,
displayedDate: new Date(),
invalidInput: false,
formats: [
{
title: 'Locale datetime',
getDate() {
return vm.displayedDate.toLocaleString();
},
dateFromFormat(dateString) {
return dateString
}
},
{
title: 'ISO 8601',
getDate() {
return vm.displayedDate.toISOString();
},
dateFromFormat(dateString) {
return new Date(dateString)
}
},
{
title: 'UTC format',
getDate() {
return vm.displayedDate.toUTCString();
},
dateFromFormat(dateString) {
return new Date(dateString)
}
},
{
title: 'UNIX Timestamp (ms)',
getDate() {
return vm.displayedDate.getTime();
},
dateFromFormat(dateString) {
return new Date(parseInt(dateString))
}
},
{
title: 'Complete',
getDate() {
return vm.displayedDate.toString();
},
dateFromFormat(dateString) {
return new Date(dateString)
}
}
],
refreshCurrentDate() {
if (this.useCurrentDate) {
this.displayedDate = new Date();
}
},
copyDate(date) {
copyToClipboard(date);
this.$toast.success('Copied to clipboard.')
},
userInputChanged() {
try {
this.invalidInput = false;
const newDate = this.formats.find(f => f.title === this.inputFormatterTitle)?.dateFromFormat(this.inputString);
if (newDate && !isNaN(newDate.getTime())) {
this.useCurrentDate = false;
this.displayedDate = newDate;
} else if (this.inputString.length > 0) {
this.invalidInput = true;
}
} catch (ignored) {
//
}
}
}
}
}
</script>
<style scoped lang="less">
</style>

View File

@@ -0,0 +1,72 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" lg="4" md="6" sm="12">
<v-card>
<v-card-title>File to Base64</v-card-title>
<v-card-text>
<FileUploader v-model="file"/>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" lg="4" md="6" sm="12" v-if="base64 || loading">
<v-card>
<v-card-title>Result</v-card-title>
<v-card-text>
<v-textarea
label="File in base 64"
outlined
readonly
v-model="base64"
hide-details
:loading="loading"
/>
<div class="text-center mt-4">
<v-btn @click="copyBase64()" depressed>Copy base64</v-btn>
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</template>
<script>
import FileUploader from '../../components/FileUploader'
import {copyToClipboard} from "../../utils/helpers";
export default {
name: "FileToBase64",
components: {FileUploader},
data() {
return {
file: undefined,
loading: false,
base64: '',
copyBase64() {
copyToClipboard(this.base64)
this.$toast.success('Copied to clipboard.')
}
}
},
methods:{
handleBase64(base64){
this.base64 = base64;
this.loading = false;
}
},
watch: {
file() {
this.loading = true;
this.base64 = '';
const reader = new FileReader();
reader.onload = () => this.handleBase64(reader.result);
reader.onerror = () => this.handleBase64('[An error as occurred]');
reader.readAsDataURL(this.file);
}
}
}
</script>
<style scoped>
</style>

73
src/routes/tools/Hash.vue Normal file
View File

@@ -0,0 +1,73 @@
<template>
<v-card class="single-card">
<v-card-title>Hash text</v-card-title>
<v-card-text>
<v-textarea
outlined
v-model="inputText"
label="Text to hash"
></v-textarea>
<v-select
:items="Object.keys(algorithms)"
label="Algorithm"
outlined
v-model="algorithm"
></v-select>
<v-textarea
outlined
readonly
v-model="hashed"
label="Hashed text"
></v-textarea>
<div class="text-center">
<v-btn depressed @click="copyHash()">Copy hash</v-btn>
</div>
</v-card-text>
</v-card>
</template>
<script>
import Vue from 'vue'
import {copyToClipboard} from "../../utils/helpers";
export default {
name: "Hash",
data() {
return {
inputText: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
algorithm: 'SHA256',
algorithms:{
'MD5': Vue.CryptoJS.MD5,
'SHA1': Vue.CryptoJS.SHA1,
'SHA256': Vue.CryptoJS.SHA256,
'SHA224': Vue.CryptoJS.SHA224,
'SHA512': Vue.CryptoJS.SHA512,
'SHA384': Vue.CryptoJS.SHA384,
'SHA3': Vue.CryptoJS.SHA3,
'RIPEMD160': Vue.CryptoJS.RIPEMD160
},
copyHash(){
copyToClipboard(this.hashed)
this.$toast.success('Copied to clipboard.')
}
}
},
computed: {
hashed() {
if(!this.algorithms[this.algorithm]){
this.$toast.error('Invalid algorithm.')
return '';
}else{
return this.algorithms[this.algorithm](this.inputText).toString();
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,95 @@
<template>
<v-card class="single-card">
<v-card-title>Cypher text</v-card-title>
<v-card-text>
<v-row justify="center" align="center">
<v-col cols="12" lg="8" md="12">
<v-textarea
outlined
v-model="key"
label="Encryption key"
rows="1"
@input="encrypt"
></v-textarea>
</v-col>
<v-col cols="12" lg="4" md="12">
<v-select
:items="Object.keys(algorithms)"
label="Algorithm"
outlined
v-model="algorithm"
@change="encrypt"
></v-select>
</v-col>
</v-row>
<v-textarea
outlined
v-model="decrypted"
label="Clear text"
@input="encrypt"
></v-textarea>
<v-textarea
outlined
v-model="encrypted"
label="Cyphered text"
@input="decrypt"
></v-textarea>
<div class="text-center">
<v-btn depressed @click="copy()">Copy result</v-btn>
</div>
</v-card-text>
</v-card>
</template>
<script>
import Vue from 'vue'
import {copyToClipboard} from "../../utils/helpers";
export default {
name: "TextCypher",
data() {
return {
decrypted: 'Lorem ipsum dolor sit amet.',
key: 'sup3r s3cr3t k3y',
encrypted: '',
algorithm: 'AES',
algorithms: {
'AES': Vue.CryptoJS.AES,
'TripleDES': Vue.CryptoJS.TripleDES,
'Rabbit': Vue.CryptoJS.Rabbit,
'RabbitLegacy': Vue.CryptoJS.RabbitLegacy,
'RC4': Vue.CryptoJS.RC4
}
};
},
mounted() {
this.encrypt();
},
methods: {
copy(text) {
copyToClipboard(text)
this.$toast.success('Copied to clipboard.')
},
encrypt() {
try {
this.encrypted = this.algorithms[this.algorithm].encrypt(this.decrypted.trim(), this.key).toString()
} catch (ignored) {
// ignored
}
},
decrypt() {
try {
this.decrypted = this.algorithms[this.algorithm].decrypt(this.encrypted.trim(), this.key).toString(Vue.CryptoJS.enc.Utf8)
} catch (ignored) {
// ignored
}
}
}
}
</script>
<style lang="less">
</style>

View File

@@ -0,0 +1,72 @@
<template>
<v-card class="single-card">
<v-card-title>Text stats</v-card-title>
<v-card-text>
<v-textarea
outlined
v-model="inputText"
label="Input text"
/>
<table>
<tr>
<td><strong>Character count:</strong></td>
<td>{{ inputText.length }}</td>
</tr>
<tr>
<td><strong>Character count (without spaces):</strong></td>
<td>{{ inputText.split(' ').join('').length }}</td>
</tr>
<tr>
<td><strong>Word count:</strong></td>
<td>{{ inputText.length > 0 ? inputText.trim().split(/\s+/).length : 0 }}</td>
</tr>
<tr>
<td><strong>Line count:</strong></td>
<td>{{ inputText.length > 0 ? inputText.split(/\r\n|\r|\n/).length : 0 }}</td>
</tr>
<tr>
<td><strong>Byte size:</strong></td>
<td>{{ formatBytes(bytesSize) }} <span v-if="bytesSize >= 1024">({{bytesSize}} Bytes)</span></td>
</tr>
</table>
</v-card-text>
</v-card>
</template>
<script>
import {formatBytes} from "../../utils/helpers";
export default {
name: "TextStats",
data() {
return {
Blob: Blob,
formatBytes,
inputText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
}
},
computed: {
bytesSize() {
return new Blob([this.inputText]).size
}
}
}
</script>
<style scoped lang="less">
table {
width: 100%;
tr {
td {
width: 50%;
padding: 5px;
&:first-child {
text-align: right;
}
}
}
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<v-card class="single-card">
<v-card-title>Token generator</v-card-title>
<v-card-text>
<v-row no-gutters>
<v-col lg="6" md="12">
<v-switch v-model="withLowercase" label="Lowercase (abc...)"/>
<v-switch v-model="withUppercase" label="Uppercase (ABC...)"/>
</v-col>
<v-col lg="6" md="12">
<v-switch v-model="withNumbers" label="Numbers (123...)"/>
<v-switch v-model="withSpecials" label="Specials (#]-...)"/>
</v-col>
</v-row>
<v-slider :label="`Length (${length})`" v-model="length" min="1" max="256"></v-slider>
<v-textarea outlined v-model="token"></v-textarea>
<div class="text-center">
<v-btn @click="refreshToken()" depressed class="mr-4">Refresh</v-btn>
<v-btn @click="copyToken()" depressed>Copy token</v-btn>
</div>
</v-card-text>
</v-card>
</template>
<script>
import {copyToClipboard} from "../../utils/helpers";
const shuffle = (str) => str.split('').sort(() => 0.5 - Math.random()).join('');
const noop = () => {
};
const lowercase = 'abcdefghijklmopqrstuvwxyz';
const uppercase = 'ABCDEFGHIJKLMOPQRSTUVWXYZ';
const numbers = '0123456789';
const specials = '.,;:!?./-"\'#{([-|\\@)]=}*+';
export default {
name: 'TokenGenerator',
data() {
return {
withNumbers: true,
withLowercase: true,
withUppercase: true,
withSpecials: false,
length: 32,
refreshBool: true,
refreshToken() {
this.refreshBool = !this.refreshBool;
},
copyToken() {
copyToClipboard(this.token);
this.$toast.success('Copied to clipboard.')
}
}
},
computed: {
token() {
if (this.refreshBool) noop(); // To force recomputation
let result = '';
if (this.withLowercase) result += lowercase;
if (this.withUppercase) result += uppercase;
if (this.withNumbers) result += numbers;
if (this.withSpecials) result += specials;
return shuffle(result.repeat(this.length)).substring(0, this.length);
}
}
}
</script>
<style >
</style>

View File

@@ -0,0 +1,81 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" lg="4" md="6" sm="12">
<v-card>
<v-card-title>URL Encode</v-card-title>
<v-card-text>
<v-textarea
outlined
label="String to encode"
v-model="encodeInput"
/>
<v-textarea
readonly
outlined
label="URL encoded string"
v-model="encodeOutput"
/>
<div class="text-center">
<v-btn @click="copyText(encodeOutput)" depressed>Copy result</v-btn>
</div>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" lg="4" md="6" sm="12">
<v-card>
<v-card-title>URL Decode</v-card-title>
<v-card-text>
<v-textarea
outlined
label="String to decode"
v-model="decodeInput"
/>
<v-textarea
readonly
outlined
label="URL decoded string"
v-model="decodeOutput"
/>
<div class="text-center">
<v-btn @click="copyText(decodeOutput)" depressed>Copy result</v-btn>
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</template>
<script>
import {copyToClipboard} from "../../utils/helpers";
export default {
name: "UrlEncoder",
data(){
return {
encodeInput: 'Hello world :)',
decodeInput: 'Hello%20world%20%3A)',
copyText(text){
copyToClipboard(text)
this.$toast.success('Copied to clipboard.')
}
}
},
computed:{
encodeOutput(){
return encodeURIComponent(this.encodeInput)
},
decodeOutput(){
return decodeURIComponent(this.encodeInput)
},
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,47 @@
<template>
<v-card class="single-card">
<v-card-title>Uuid v4 generator</v-card-title>
<v-card-text>
<v-text-field outlined v-model="token" class="centered-input"/>
<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>
</div>
</v-card-text>
</v-card>
</template>
<script>
import {copyToClipboard} from "../../utils/helpers";
const noop = () => {
};
export default {
name: "UuidGenerator",
data: () => ({
refreshBool: true
}),
methods: {
copyToken() {
copyToClipboard(this.token);
this.$toast.success('Copied to clipboard.')
}
},
computed: {
token() {
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));
}
}
}
</script>
<style scoped>
::v-deep .centered-input input {
text-align: center
}
</style>

31
src/utils/helpers.js Normal file
View File

@@ -0,0 +1,31 @@
const copyToClipboard = (text) => {
const input = document.createElement('textarea');
input.innerHTML = text;
document.body.appendChild(input);
input.select();
const result = document.execCommand('copy');
document.body.removeChild(input);
return result;
}
const fileIsImage = (file) => {
return file.type.split('/')[0] === 'image';
}
const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
export {
copyToClipboard,
fileIsImage,
formatBytes
}

View File

@@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@@ -1,18 +0,0 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
components: {
HelloWorld
}
}
</script>

18
vue.config.js Normal file
View File

@@ -0,0 +1,18 @@
const webpack = require('webpack');
module.exports = {
"transpileDependencies": [
"vuetify"
],
configureWebpack: () => {
return {
plugins: [
new webpack.DefinePlugin({
'process.env': {
'APPLICATION_VERSION': JSON.stringify(require('./package.json').version),
}
})
]
}
}
}