2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -4,7 +4,7 @@
|
||||
<!--- In case it's a feature suggestion, otherwise delete this section -->
|
||||
<!--- Make sure there isn't already a snippet accomplishing your goal -->
|
||||
## [FEATURE] _REPLACE THIS WITH A BRIEF SUMMARY OF THE SUGGESTED SNIPPET_
|
||||
**Category:** <!-- One of the existing categories preferrably -->
|
||||
**Category:** <!-- One of the existing categories preferably -->
|
||||
### Description <!-- IF NEEDED -->
|
||||
<!-- More detailed description of the snippet you want to be included in 30-seconds-of-code -->
|
||||
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -13,6 +13,7 @@
|
||||
- [ ] Website
|
||||
- [ ] Snippets
|
||||
- [ ] General / Things regarding the repository (like CI Integration)
|
||||
- [ ] Tests
|
||||
|
||||
## Types of changes
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,5 +3,4 @@ currentSnippet\.js
|
||||
*.md.temp.js
|
||||
.idea
|
||||
test.sh
|
||||
dist/
|
||||
test/
|
||||
/*.log
|
||||
|
||||
@ -12,6 +12,8 @@ script:
|
||||
- npm run linter
|
||||
- npm run builder
|
||||
- npm run webber
|
||||
- npm run packager
|
||||
- npm run tester
|
||||
after_success:
|
||||
- chmod +x .travis/push.sh
|
||||
- .travis/push.sh
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
setup_git() {
|
||||
git config --global user.email "travis@travis-ci.org"
|
||||
git config --global user.name "Travis CI"
|
||||
git config --global user.email "30secondsofcode@gmail.com"
|
||||
git config --global user.name "30secondsofcode"
|
||||
}
|
||||
|
||||
commit_website_files() {
|
||||
if [ $TRAVIS_EVENT_TYPE != "pull_request" ]; then
|
||||
if [ $TRAVIS_BRANCH == "master" ]; then
|
||||
echo "Commiting to master branch..."
|
||||
echo "Committing to master branch..."
|
||||
git checkout master
|
||||
git add *
|
||||
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER [ci skip]"
|
||||
if [ $TRAVIS_EVENT_TYPE == "cron" ]; then
|
||||
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER [cron]"
|
||||
elif [ $TRAVIS_EVENT_TYPE == "api" ]; then
|
||||
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER [custom]"
|
||||
else
|
||||
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@ -8,17 +8,17 @@ As a member of the team that manages **30 seconds of code**, you have the follow
|
||||
|
||||
- **Be part of the conversation in the issue tracker.** That includes (but is not limited to) helping out new members, discussing new features and explaining decisions to people.
|
||||
- **Review pull requests.** You do not have to read through all of the pull requests and review them, but taking the time each day to review a few can help a great deal.
|
||||
- **Be civil and polite.** If you are about to lose your temper, take a step back and go do something else. We want our interactions with the community to be polite, so that more people can join the project and contribute in any way they can. Remember to always thank contributors for their help, even if it's minor changes or changes that did not make it into the project. This way we can reward and encourage people to keep being part of the community.
|
||||
- **Be civil and polite.** If you are about to lose your temper, take a step back and do something else. We want our interactions with the community to be polite so that more people can join the project and contribute in any way they can. Remember to always thank contributors for their help, even if it's minor changes or changes that did not make it into the project. This way we can reward and encourage people to keep being part of the community.
|
||||
- **Contribute when you want, moderate when you can.** If you have a lot on your plate outside of this project, it's alright. It's better to take a break for a few days rather than hastily deal with issues and pull requests that might break things.
|
||||
|
||||
## Guidelines for merging pull requests and making changes to the project
|
||||
|
||||
- **[Usual guidelines](https://github.com/Chalarangelo/30-seconds-of-code/blob/master/CONTRIBUTING.md) apply.** Make sure to follow them, like everybody else.
|
||||
- **For a pull request to be considered ready to merge, there should be at least 2 (preferrably 3) reviews approving it for merge.** There are, however, certain exceptions:
|
||||
- **If a pull request only fixes typos**, there is no need to wait for a second reviewer (unless you are not absolutely certain these were not typos in the first place).
|
||||
- **If a pull request only clarifies a snippet's description or enforces the styleguide for an existng snippet**, you might be able to merge it without getting a second reviewer to review it, but only if you are absolutely certain about it.
|
||||
- **For a pull request to be considered ready to merge, there should be at least 2 (preferably 3) reviews approving it for merge.** There are, however, certain exceptions:
|
||||
- **If a pull request only fixes typos**, there is no need to wait for a second reviewer (unless you are not certain these were not typos in the first place).
|
||||
- **If a pull request only clarifies a snippet's description or enforces the style guide for an existing snippet**, you might be able to merge it without getting a second reviewer to review it, but only if you are certain about it.
|
||||
- **Make sure pull requests pass the Travis CI build**, otherwise try and find out what's wrong and inform the author of the pull request.
|
||||
- **Changes to build scripts, guidelines and things that might break the processes we have in place need to be reviewed by [@Chalarangelo](https://github.com/Chalarangelo)** (this is temporary, but we need a baseline to make sure we break as few things as possible in the beginning).
|
||||
- **After merging a pull request, make sure to check for untagged snippets and tag them appropriately.** Try to keep all snippets tagged, so that the list and website are up to date.
|
||||
- **If you make changes or additions to existing snippets or if you want to add you own snippets, you will go through the pull request process that everyone else goes.** Exceptions apply similarly to the ones mentioned above about merging pull requests (i.e. typos, description clarification and the way script and build process changes are handled). Pull requests suggested by collaborators should be reviewed by at least two other collaborators to be considered ready to merge.
|
||||
- **If you make changes or additions to existing snippets or if you want to add your own snippets, you will go through the pull request process that everyone else goes.** Exceptions apply similarly to the ones mentioned above about merging pull requests (i.e. typos, description clarification and the way script and build process changes are handled). Pull requests suggested by collaborators should be reviewed by at least two other collaborators to be considered ready to merge.
|
||||
- **Pull requests that are inactive for over a week should be closed or put on hold.**
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
## Contributing
|
||||

|
||||
|
||||
**30 seconds of code** is a community effort, so feel free to contribute in any way you can. Every contribution helps!
|
||||
|
||||
Here's what you can do to help:
|
||||
|
||||
- Submit [pull requests](https://github.com/Chalarangelo/30-seconds-of-code/pulls) with snippets and tests that you have created (see below for guidelines).
|
||||
- [Open issues](https://github.com/Chalarangelo/30-seconds-of-code/issues/new) for things you want to see added or modified.
|
||||
- Be part of the discussion by helping out with [existing issues](https://github.com/Chalarangelo/30-seconds-of-code/issues) or talking on our [gitter channel](https://gitter.im/30-seconds-of-code/Lobby).
|
||||
- Submit [pull requests](https://github.com/Chalarangelo/30-seconds-of-code/pulls) with snippets you have created (see below for guidelines).
|
||||
- Tag uncategorized snippets by running `npm run tagger` and adding the appropriate tag next to the script name in `tag_database`.
|
||||
- Tag uncategorized snippets by running `npm run tagger` and adding the appropriate tags next to the script name in `tag_database`.
|
||||
- Fix typos in existing snippets, improve snippet descriptions and explanations or provide better examples.
|
||||
- Write tests for existing snippets (see below for guidelines).
|
||||
|
||||
### Snippet submission and Pull request guidelines
|
||||
|
||||
- **DO NOT MODIFY THE README.md FILE!** Make changes to individual snippet files. You can optionally run `npm run builder` to update the README.md file automatically, based on the changes you have made.
|
||||
- **DO NOT MODIFY THE index.html FILE!** Make changes to individual snippet files. You can optionally run `npm run webber` to update the index.md file automatically, based on the changes you have made.
|
||||
- **DO NOT MODIFY THE README.md or index.html FILES!** Make changes to individual snippet files. **Travis CI** will automatically build the `README.md` and `index.html` files when your pull request is merged.
|
||||
- **Snippet filenames** must correspond to the title of the snippet. For example, if your snippet is titled `### awesomeSnippet` the filename should be `awesomeSnippet.md`.
|
||||
- Use `camelCase`, not `kebab-case` or `snake_case`.
|
||||
- Avoid capitalization of words, except if the whole word is capitalized (e.g. `URL` should be capitalized in the filename and the snippet title).
|
||||
- **Snippet titles** should have be the same as the name of the function that is present in the snippet.
|
||||
- **Snippet titles** should be the same as the name of the function that is present in the snippet.
|
||||
- All snippet titles must be prefixed with `###` and be at the very first line of your snippet.
|
||||
- Snippet titles must be unique (although if you cannot find a better title, just add some placeholder at the end of the filename and title and we will figure it out).
|
||||
- Follow snippet titles with an empty line.
|
||||
@ -26,7 +26,7 @@ Here's what you can do to help:
|
||||
- **Snippet code** must be enclosed inside ` ```js ` and ` ``` `.
|
||||
- Remember to start your snippet's code on a new line below the opening backticks.
|
||||
- Use ES6 notation to define your function. For example `const myFunction = ( arg1, arg2 ) => { }`.
|
||||
- Please use Javacript [Semi-Standard Style](https://github.com/Flet/semistandard).
|
||||
- Please use Javascript [Semi-Standard Style](https://github.com/Flet/semistandard).
|
||||
- Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting.
|
||||
- All snippets must be followed by one (more if necessary) test case after the code, in a new block enclosed inside ` ```js ` and ` ``` `. The syntax for this is `myFunction('testInput') // 'testOutput'`. Use multiline examples only if necessary.
|
||||
- Try to make your function name unique, so that it does not conflict with existing snippets.
|
||||
@ -36,7 +36,19 @@ Here's what you can do to help:
|
||||
- Snippets *should* be abstract enough to be applied to different scenarios.
|
||||
- It is not mandatory but highly appreciated if you provide **test cases** and/or performance tests (we recommend using [jsPerf](https://jsperf.com/)).
|
||||
- You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets.
|
||||
- Updating the index.html or README.md files should only be done by altering the scripts in the **scripts** folder or altering their relative static parts in the **static-parts** folder.
|
||||
|
||||
### Writing tests
|
||||
- Before writing any tests run `npm run tdd` script. It will update test directory to include new snippets as well as update old ones if needed.
|
||||
- **DO NOT MODIFY THE snippetName.js files** under test directory.
|
||||
- We are using [tape](https://github.com/substack/tape) for testing.
|
||||
- Write tests under `snippetName.test.js` file. If you have trouble doing so, check out tests of other snippets.
|
||||
- Be sure to run `npm run test`. It is going to run all tests for all snippets.
|
||||
- Make a new pull request **only if all the tests are passing**.
|
||||
|
||||
#### Browser specific tests
|
||||
- If your snippet belongs to `browser` category, then you will need to modify the tests to make them work.
|
||||
- By default, `Node.js` isn't browser environment. That said we have to use an external package to help us simulate the browser for our tests.
|
||||
- We use [jsdom](https://www.npmjs.com/package/jsdom) for our browser specific tests. You can find their [documentation](https://github.com/jsdom/jsdom) on GitHub as well.
|
||||
|
||||
### Additional guidelines and conventions regarding snippets
|
||||
|
||||
@ -49,13 +61,13 @@ Here's what you can do to help:
|
||||
- Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions to convention are:
|
||||
- `arr` for arrays (usually as the snippet function's argument).
|
||||
- `str` for strings.
|
||||
- `n` for a numeric value (usually as the snippet function's argument).
|
||||
- `num` or `n` for a numeric value (usually as the snippet function's argument).
|
||||
- `el` for DOM elements (usually as the snippet function's argument).
|
||||
- `val` or `v` for value (usually when iterating a list, mapping, sorting etc.).
|
||||
- `acc` for accumulators in `Array.reduce()`.
|
||||
- `(a,b)` for the two values compared when using `Array.sort()`.
|
||||
- `i` for indexes.
|
||||
- `func` for function arguments.
|
||||
- `fn` for function arguments.
|
||||
- `nums` for arrays of numbers.
|
||||
- Use `()` if your function takes no arguments.
|
||||
- Use `_` if an argument inside some function (e.g. `Array.reduce()`) is not used anywhere in your code.
|
||||
|
||||
1
advanced.svg
Normal file
1
advanced.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="78" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect height="20" rx="3" fill="#fff" width="64"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h65v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">advanced</text><text x="325" y="140" transform="scale(.1)" textLength="530">advanced</text></g> </svg>
|
||||
|
After Width: | Height: | Size: 695 B |
15
dist/NOTICE.md
vendored
Normal file
15
dist/NOTICE.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# WARNING!
|
||||
|
||||
The `_30s` module is not production ready. Do NOT use it in production websites.
|
||||
It is strictly for testing purposes at this moment in time. Snippets do not have
|
||||
any unit tests written and will not be reliable.
|
||||
|
||||
Snippet names can and will change without notice between minor versions.
|
||||
|
||||
Given the version `0.x.y`:
|
||||
|
||||
* `x` indicates a snippet name change.
|
||||
* `y` indicates a new snippet or fix.
|
||||
|
||||
If your project is not serious and you do not care about the above issues, you will want
|
||||
to use the `es5` version and also include `babel-polyfill` for widest browser support.
|
||||
2252
dist/_30s.es5.js
vendored
Normal file
2252
dist/_30s.es5.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/_30s.es5.min.js
vendored
Normal file
1
dist/_30s.es5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1293
dist/_30s.esm.js
vendored
Normal file
1293
dist/_30s.esm.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1301
dist/_30s.js
vendored
Normal file
1301
dist/_30s.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/_30s.min.js
vendored
Normal file
1
dist/_30s.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/clipboard.svg
Normal file
1
docs/clipboard.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f8f8f8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clipboard"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>
|
||||
|
After Width: | Height: | Size: 367 B |
2610
docs/index.html
2610
docs/index.html
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -336,16 +336,19 @@ mark {
|
||||
border-top: 0;
|
||||
}
|
||||
& > label:first-of-type {
|
||||
border-radius: var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var}) 0 0; // universalize
|
||||
border-radius: var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var}) 0 0;
|
||||
}
|
||||
& > label:last-of-type {
|
||||
border-radius: 0 0 var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var}); // universalize
|
||||
& > label:last-of-type:not(:first-of-type) {
|
||||
border-radius: 0 0 var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var});
|
||||
}
|
||||
& > :checked:last-of-type + label {
|
||||
& > label:last-of-type:first-of-type {
|
||||
border-radius: var(#{$universal-border-radius-var});
|
||||
}
|
||||
& > :checked:last-of-type:not(:first-of-type) + label {
|
||||
border-radius: 0;
|
||||
+ div {
|
||||
border-radius: 0 0 var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var}); // universalize
|
||||
}
|
||||
}
|
||||
& > :checked:last-of-type + label + div {
|
||||
border-radius: 0 0 var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
113
docs/mini/_progress.scss
Normal file
113
docs/mini/_progress.scss
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Definitions for progress elements and spinners.
|
||||
*/
|
||||
$progress-back-color: #ddd !default; // Background color of <progress>.
|
||||
$progress-fore-color: #555 !default; // Foreground color of <progress>.
|
||||
$progress-height: 0.75rem !default; // Height of <progress>.
|
||||
$progress-max-value: 1000 !default; // Arithmetic max value of <progress> - use integer values.
|
||||
$progress-inline-name: 'inline' !default; // Class name for inline <progress> elements.
|
||||
$progress-inline-width: 60% !default; // Width of inline <progress> elements.
|
||||
$_include-spinner-donut: true !default; // [Hidden] Should spinner donuts be included? (boolean)
|
||||
$spinner-donut-name: 'spinner' !default; // Class name for spinner donuts
|
||||
$spinner-donut-size: 1.25rem !default; // Size of the spinner donuts
|
||||
$spinner-donut-border-thickness: 0.25rem !default; // Border thickness for spinner donuts
|
||||
$spinner-donut-back-color: #ddd !default; // Background color for spinner donuts
|
||||
$spinner-donut-fore-color: #555 !default; // Foreground color for spinner donuts
|
||||
// CSS variable name definitions [exercise caution if modifying these]
|
||||
$progress-back-color-var: '--progress-back-color' !default;
|
||||
$progress-fore-color-var: '--progress-fore-color' !default;
|
||||
$spinner-donut-back-color-var: '--spinner-back-color' !default;
|
||||
$spinner-donut-fore-color-var: '--spinner-fore-color' !default;
|
||||
// == Uncomment below code if this module is used on its own ==
|
||||
//
|
||||
// $universal-margin: 0.5rem !default; // Universal margin for the most elements
|
||||
// $universal-border-radius: 0.125rem !default; // Universal border-radius for most elements
|
||||
// $universal-box-shadow: none !default; // Universal box-shadow for most elements
|
||||
// $universal-margin-var: '--universal-margin' !default;
|
||||
// $universal-border-radius-var: '--universal-border-radius' !default;
|
||||
// $universal-box-shadow-var: '--universal-box-shadow' !default;
|
||||
// :root {
|
||||
// #{$universal-margin-var}: $universal-margin;
|
||||
// #{$universal-border-radius-var}: $universal-border-radius;
|
||||
// @if $universal-box-shadow != none {
|
||||
// #{$universal-box-shadow-var}: $universal-box-shadow;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ============================================================
|
||||
// Check the `_progress_mixins.scss` file to find this module's mixins.
|
||||
@import '_progress_mixins';
|
||||
/* Progess module CSS variable definitions */
|
||||
:root {
|
||||
#{$progress-back-color-var}: $progress-back-color;
|
||||
#{$progress-fore-color-var}: $progress-fore-color;
|
||||
}
|
||||
// Default styling for progress. Use mixins for alternate styles
|
||||
progress {
|
||||
display: block;
|
||||
vertical-align: baseline; // Correct vertical alignment in some browsers.
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
height: $progress-height;
|
||||
width: calc(100% - 2 * var(#{$universal-margin-var}));
|
||||
margin: var(#{$universal-margin-var});
|
||||
border: 0; // Removes default border
|
||||
border-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
@if $universal-box-shadow != none {
|
||||
box-shadow: var(#{$universal-box-shadow-var});
|
||||
}
|
||||
background: var(#{$progress-back-color-var});
|
||||
color: var(#{$progress-fore-color-var});
|
||||
// Foreground color on webkit browsers
|
||||
&::-webkit-progress-value {
|
||||
background: var(#{$progress-fore-color-var});
|
||||
border-top-left-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
border-bottom-left-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
}
|
||||
// Background color on webkit browser
|
||||
&::-webkit-progress-bar {
|
||||
background: var(#{$progress-back-color});
|
||||
}
|
||||
// Foreground color on Firefox
|
||||
&::-moz-progress-bar {
|
||||
background: var(#{$progress-fore-color-var});
|
||||
border-top-left-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
border-bottom-left-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
}
|
||||
&[value="#{$progress-max-value}"] {
|
||||
&::-webkit-progress-value {
|
||||
border-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
}
|
||||
&::-moz-progress-bar {
|
||||
border-radius: calc(2 * var(#{$universal-border-radius-var}));
|
||||
}
|
||||
}
|
||||
&.#{$progress-inline-name} {
|
||||
display: inline-block;
|
||||
vertical-align: middle; // Align progress bar vertically to look better with text next to it.
|
||||
width: $progress-inline-width;
|
||||
}
|
||||
}
|
||||
// Style for donut spinner
|
||||
@if $_include-spinner-donut {
|
||||
:root {
|
||||
#{$spinner-donut-back-color-var}: $spinner-donut-back-color;
|
||||
#{$spinner-donut-fore-color-var}: $spinner-donut-fore-color;
|
||||
}
|
||||
// Donut spinner animation
|
||||
@keyframes spinner-donut-anim {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg);}
|
||||
}
|
||||
.#{$spinner-donut-name} {
|
||||
display: inline-block;
|
||||
margin: var(#{$universal-margin-var});
|
||||
border: $spinner-donut-border-thickness solid var(#{$spinner-donut-back-color-var});
|
||||
border-left: $spinner-donut-border-thickness solid var(#{$spinner-donut-fore-color-var});
|
||||
border-radius: 50%;
|
||||
width: $spinner-donut-size;
|
||||
height: $spinner-donut-size;
|
||||
animation: spinner-donut-anim 1.2s linear infinite;
|
||||
}
|
||||
}
|
||||
32
docs/mini/_progress_mixins.scss
Normal file
32
docs/mini/_progress_mixins.scss
Normal file
@ -0,0 +1,32 @@
|
||||
// Progress module's mixin definitions are here. For the module itself
|
||||
// check `progress.scss`.
|
||||
// Progress color variant mixin:
|
||||
// $progress-alt-name: The name of the class used for the <progress> variant.
|
||||
// $progress-alt-fore-color: Foregound color for <progress> variant.
|
||||
// $progress-alt-back-color: Background color for <progress> variant.
|
||||
@mixin make-progress-alt-color ($progress-alt-name, $progress-alt-fore-color : $progress-fore-color,
|
||||
$progress-alt-back-color : $progress-back-color) {
|
||||
progress.#{$progress-alt-name} {
|
||||
@if $progress-alt-fore-color != $progress-fore-color{
|
||||
#{$progress-fore-color-var}: $progress-alt-fore-color;
|
||||
}
|
||||
@if $progress-alt-back-color != $progress-back-color {
|
||||
#{$progress-back-color-var}: $progress-alt-back-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Spinner donut color variant mixin:
|
||||
// $spinner-donut-alt-name: The name of the class used for the spinner donut variant.
|
||||
// $spinner-donut-alt-fore-color: Text color for spinner donut variant.
|
||||
// $spinner-donut-alt-back-color: Background color for spinner donut variant.
|
||||
@mixin make-spinner-donut-alt-color ($spinner-donut-alt-name, $spinner-donut-alt-fore-color : $spinner-donut-fore-color,
|
||||
$spinner-donut-alt-back-color : $spinner-donut-back-color) {
|
||||
.#{$spinner-donut-name}.#{$spinner-donut-alt-name} {
|
||||
@if $spinner-donut-alt-fore-color != $spinner-donut-fore-color{
|
||||
#{$spinner-donut-fore-color-var}: $spinner-donut-alt-fore-color;
|
||||
}
|
||||
@if $spinner-donut-alt-back-color != $spinner-donut-back-color {
|
||||
#{$spinner-donut-back-color-var}: $spinner-donut-alt-back-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
321
docs/mini/_table.scss
Normal file
321
docs/mini/_table.scss
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
Definitions for the responsive table component.
|
||||
*/
|
||||
// The tables use the common table elements and syntax.
|
||||
/*
|
||||
$table-mobile-breakpoint: 767px !default; // Breakpoint for table mobile view.
|
||||
$table-mobile-card-spacing: 10px !default; // Space between <tr> cards - mobile view.
|
||||
$table-mobile-card-label: 'data-label' !default;// Attribute used to replace column headers in mobile view.
|
||||
$table-not-responsive-name: 'preset' !default; // Class name for table non-responsive view.
|
||||
$include-horizontal-table: true !default; // Should horizontal tables be included? (`true`/`false`)
|
||||
$table-horizontal-name: 'horizontal' !default;// Class name for table horizontal view.
|
||||
$include-scrollable-table: true !default; // Should scrollable tables be included? (`true`/`false`)
|
||||
$table-scrollable-name: 'scrollable' !default;// Class name for table scrollable view.
|
||||
$table-scrollable-height: 400px !default; // Height for table scrollable view.
|
||||
$include-striped-table: true !default; // [Hidden flag] Should striped tables be included? (`true`/`false`)
|
||||
$table-striped-name: 'striped' !default; // Class name for striped table.
|
||||
// External variables' defaults are used only if you import this module on its own, without the rest of the framework.
|
||||
$back-color: white !default; // [External variable - core] Background color for everything.
|
||||
$fore-color: black !default; // [External variable - core] Foreground color for everything.
|
||||
*/
|
||||
$table-mobile-breakpoint: 768px !default;
|
||||
$table-max-height: 400px !default;
|
||||
$table-caption-font-size: 1.5rem !default;
|
||||
$table-mobile-card-label: 'data-label' !default;
|
||||
$table-mobile-label-font-weight: 600 !default;
|
||||
|
||||
$table-border-color: #aaa !default;
|
||||
$table-border-separator-color: #666 !default;
|
||||
|
||||
$_include-horizontal-table: true !default;
|
||||
$table-horizontal-name: 'horizontal' !default;
|
||||
|
||||
// CSS variable name definitions [exercise caution if modifying these]
|
||||
$table-border-color-var: '--table-border-color' !default;
|
||||
$table-border-separator-color-var: '--table-border-separator-color' !default;
|
||||
// == Uncomment below code if this module is used on its own ==
|
||||
//
|
||||
// $universal-margin: 0.5rem !default; // Universal margin for the most elements
|
||||
// $universal-padding: 0.5rem !default; // Universal padding for the most elements
|
||||
// $universal-border-radius: 0.125rem !default; // Universal border-radius for most elements
|
||||
// $universal-box-shadow: none !default; // Universal box-shadow for most elements
|
||||
// $universal-margin-var: '--universal-margin' !default;
|
||||
// $universal-padding-var: '--universal-padding' !default;
|
||||
// $universal-border-radius-var: '--universal-border-radius' !default;
|
||||
// $universal-box-shadow-var: '--universal-box-shadow' !default;
|
||||
// :root {
|
||||
// #{$universal-margin-var}: $universal-margin;
|
||||
// #{$universal-padding-var}: $universal-padding;
|
||||
// #{$universal-border-radius-var}: $universal-border-radius;
|
||||
// @if $universal-box-shadow != none {
|
||||
// #{$universal-box-shadow-var}: $universal-box-shadow;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ============================================================
|
||||
/* Table module CSS variable definitions. */
|
||||
:root {
|
||||
#{$table-border-color-var}: $table-border-color;
|
||||
#{$table-border-separator-color-var}: $table-border-separator-color;
|
||||
}
|
||||
// Desktop view.
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
flex-flow: row wrap;
|
||||
padding: var(#{$universal-padding-var});
|
||||
padding-top: 0;
|
||||
@if not($_include-horizontal-table) {
|
||||
overflow: auto;
|
||||
max-height: $table-max-height;
|
||||
}
|
||||
caption {
|
||||
font-size: $table-caption-font-size;
|
||||
margin: calc(2 * var(#{$universal-margin-var})) 0;
|
||||
max-width: 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
thead, tbody {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
border: $__1px solid var(#{$table-border-color-var});
|
||||
@if not($_include-horizontal-table) {
|
||||
max-width: 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
}
|
||||
thead {
|
||||
z-index: 999; // Fixes the visibility of the element.
|
||||
border-radius: var(#{$universal-border-radius-var}) var(#{$universal-border-radius-var}) 0 0;
|
||||
border-bottom: $__1px solid var(#{$table-border-separator-color-var}); // var This
|
||||
@if not($_include-horizontal-table) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
border-top: 0;
|
||||
margin-top: calc(0 - var(#{$universal-margin-var})); // might be useless
|
||||
border-radius: 0 0 var(--universal-border-radius) var(--universal-border-radius);
|
||||
}
|
||||
tr {
|
||||
display: flex;
|
||||
padding: 0; // Apply always to overwrite default.
|
||||
@if not($_include-horizontal-table) {
|
||||
flex-flow: row wrap;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
}
|
||||
th, td {
|
||||
padding: calc(2 * var(#{$universal-padding-var})); // Apply always to overwrite default.
|
||||
@if not($_include-horizontal-table) {
|
||||
flex: 1 0 0%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
background: #e6e6e6; // use vars
|
||||
color: #111; // vars
|
||||
}
|
||||
td {
|
||||
background: #fafafa; // use variables, this is a test (body)
|
||||
border-top: $__1px solid var(#{$table-border-color-var});
|
||||
}
|
||||
@if not($_include-horizontal-table) {
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Styling for horizntal tables
|
||||
@if $_include-horizontal-table {
|
||||
table:not(.#{$table-horizontal-name}) {
|
||||
overflow: auto;
|
||||
max-height: $table-max-height;
|
||||
thead, tbody {
|
||||
max-width: 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
tr {
|
||||
flex-flow: row wrap;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
th, td {
|
||||
flex: 1 0 0%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
table.#{$table-horizontal-name} {
|
||||
border: 0;
|
||||
thead, tbody {
|
||||
border: 0;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
tbody {
|
||||
overflow: auto;
|
||||
justify-content: space-between;
|
||||
flex: 1 0 0;
|
||||
margin-left: calc( 4 * var(#{$universal-margin-var}));
|
||||
padding-bottom: calc(var(#{$universal-padding-var}) / 4);
|
||||
}
|
||||
tr {
|
||||
flex-direction: column;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
th, td {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: $__1px solid var(#{$table-border-color-var});
|
||||
&:not(:first-child){
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
th {
|
||||
text-align: right;
|
||||
border-left: $__1px solid var(#{$table-border-color-var});
|
||||
border-right: $__1px solid var(#{$table-border-separator-color-var});
|
||||
}
|
||||
thead {
|
||||
tr:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
th:first-child, td:first-child {
|
||||
border-top: $__1px solid var(#{$table-border-color-var});
|
||||
}
|
||||
tbody tr:last-child td {
|
||||
border-right: 1px solid #aaa;
|
||||
&:first-child{
|
||||
border-top-right-radius: 0.25rem;
|
||||
}
|
||||
&:last-child{
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
thead tr:first-child th {
|
||||
&:first-child{
|
||||
border-top-left-radius: 0.25rem;
|
||||
}
|
||||
&:last-child{
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mobile
|
||||
@media screen and (max-width: #{$table-mobile-breakpoint - 1px}){
|
||||
@if $_include-horizontal-table {
|
||||
table, table.#{$table-horizontal-name} {
|
||||
border-collapse: collapse;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
display: table;
|
||||
// Accessibility (element is not visible, but screen readers read it normally)
|
||||
thead, th {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
clip: rect(0 0 0 0);
|
||||
-webkit-clip-path: inset(100%);
|
||||
clip-path: inset(100%);
|
||||
}
|
||||
tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
tr {
|
||||
display: block;
|
||||
border: $__1px solid var(#{$table-border-color-var});
|
||||
border-radius: var(#{$universal-border-radius-var});
|
||||
@if $universal-box-shadow != none {
|
||||
box-shadow: var(#{$universal-box-shadow-var});
|
||||
}
|
||||
background: #fafafa; // use variables, this is a test (body)
|
||||
padding: var(#{$universal-padding-var});
|
||||
margin: var(#{$universal-margin-var});
|
||||
margin-bottom: calc(2 * var(#{$universal-margin-var}));
|
||||
}
|
||||
th, td {
|
||||
width: auto;
|
||||
}
|
||||
td {
|
||||
display: block;
|
||||
border: 0;
|
||||
text-align: right;
|
||||
}
|
||||
td:before {
|
||||
content: attr(#{$table-mobile-card-label});
|
||||
float: left;
|
||||
font-weight: $table-mobile-label-font-weight;
|
||||
}
|
||||
th:first-child, td:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
tbody tr:last-child td {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@else {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
display: table;
|
||||
// Accessibility (element is not visible, but screen readers read it normally)
|
||||
thead, th {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
clip: rect(0 0 0 0);
|
||||
-webkit-clip-path: inset(100%);
|
||||
clip-path: inset(100%);
|
||||
}
|
||||
tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
tr {
|
||||
display: block;
|
||||
border: $__1px solid var(#{$table-border-color-var});
|
||||
border-radius: var(#{$universal-border-radius-var});
|
||||
@if $universal-box-shadow != none {
|
||||
box-shadow: var(#{$universal-box-shadow-var});
|
||||
}
|
||||
background: #fafafa; // use variables, this is a test (body)
|
||||
padding: var(#{$universal-padding-var});
|
||||
margin: var(#{$universal-margin-var});
|
||||
margin-bottom: calc(2 * var(#{$universal-margin-var}));
|
||||
}
|
||||
td {
|
||||
display: block;
|
||||
border: 0;
|
||||
text-align: right;
|
||||
}
|
||||
td:before {
|
||||
content: attr(#{$table-mobile-card-label});
|
||||
float: left;
|
||||
font-weight: $table-mobile-label-font-weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,44 +7,92 @@
|
||||
Maintainers: Angelos Chalaris
|
||||
mini.css version: v3.0.0-alpha.2
|
||||
*/
|
||||
|
||||
$fore-color-var: '--f-col';
|
||||
$secondary-fore-color-var: '--f-col2';
|
||||
$back-color-var: '--b-col';
|
||||
$secondary-back-color-var: '--b-col2';
|
||||
$blockquote-color-var: '--blq-col';
|
||||
$pre-color-var: '--pre-col';
|
||||
$border-color-var: '--br-col';
|
||||
$secondary-border-color-var: '--br-col2';
|
||||
$heading-ratio-var: '--h-ratio';
|
||||
$universal-margin-var: '--u-m';
|
||||
$universal-padding-var: '--u-p';
|
||||
$universal-border-radius-var: '--u-br-r';
|
||||
$universal-box-shadow-var: '--u-bx-shd';
|
||||
$a-link-color-var: '--a-l-col';
|
||||
$a-visited-color-var: '--a-v-col';
|
||||
|
||||
$code-font-size: 0.8125em;
|
||||
|
||||
@import 'core';
|
||||
|
||||
$grid-container-name: 'container';
|
||||
$grid-row-name: 'row';
|
||||
$grid-row-parent-layout-prefix:'cols';
|
||||
$grid-column-prefix: 'col';
|
||||
$grid-column-offset-suffix: 'o';
|
||||
$grid-order-normal-suffix: 'n';
|
||||
$grid-order-first-suffix: 'f';
|
||||
$grid-order-last-suffix: 'l';
|
||||
$grid-small-prefix: 'sm';
|
||||
$grid-medium-prefix: 'md';
|
||||
$grid-large-prefix: 'lg';
|
||||
|
||||
$card-fore-color-var: '--cd-f-col';
|
||||
$card-back-color-var: '--cd-b-col';
|
||||
$card-border-color-var: '--cd-br-col';
|
||||
|
||||
$_include-parent-layout: false;
|
||||
|
||||
@import 'layout';
|
||||
|
||||
/*
|
||||
Custom elements for card elements.
|
||||
*/
|
||||
$card-small-name: 'small'; // Class name for small cards.
|
||||
$card-small-width: 240px; // Width for small cards.
|
||||
@include make-card-alt-size ($card-small-name, $card-small-width);
|
||||
|
||||
$card-large-name: 'large'; // Class name for large cards.
|
||||
$card-large-width: 480px; // Width for large cards.
|
||||
@include make-card-alt-size ($card-large-name, $card-large-width);
|
||||
|
||||
$card-fluid-name: 'fluid'; // Class name for fluid cards.
|
||||
$card-fluid-width: 100%; // Width for fluid cards.
|
||||
@include make-card-alt-size ($card-fluid-name, $card-fluid-width);
|
||||
|
||||
$card-warning-name: 'warning'; // Class name for card warnging color variant.
|
||||
$card-warning-back-color: #ffca28; // Background color for card warnging color variant.
|
||||
$card-warning-fore-color: #111; // Text color for card warnging color variant.
|
||||
$card-warning-border-color: #e8b825; // Border style for card warnging color variant.
|
||||
@include make-card-alt-color ($card-warning-name, $card-warning-back-color, $card-warning-fore-color, $card-warning-border-color);
|
||||
|
||||
$card-error-name: 'error'; // Class name for card error color variant.
|
||||
$card-error-back-color: #b71c1c; // Background color for card error color variant.
|
||||
$card-error-fore-color: #f8f8f8; // Text color for card error color variant.
|
||||
$card-error-border-color: #a71a1a; // Border style for card error color variant.
|
||||
@include make-card-alt-color ($card-error-name, $card-error-back-color, $card-error-fore-color, $card-error-border-color);
|
||||
|
||||
$card-section-dark-name: 'dark'; // Class name for card dark section variant.
|
||||
$card-section-dark-back-color: #e0e0e0; // Background color for card dark section variant.
|
||||
$card-section-dark-fore-color: #111; // Text color for card dark section variant.
|
||||
@include make-card-section-alt-color ($card-section-dark-name, $card-section-dark-back-color, $card-section-dark-fore-color);
|
||||
|
||||
$card-section-double-padded-name: 'double-padded'; // Class name for card double-padded section variant.
|
||||
$card-section-double-padded-padding: calc(1.5 * var(#{$universal-padding-var})); // Padding for card sectiondouble-padded section variant.
|
||||
@include make-card-section-alt-style ($card-section-double-padded-name, $card-section-double-padded-padding);
|
||||
@include make-card-section-alt-style($card-section-double-padded-name, $card-section-double-padded-padding);
|
||||
|
||||
.#{$card-name} {
|
||||
box-shadow: 0 1.25rem 2.5rem -0.625rem rgba(0, 32, 64, 0.1);
|
||||
}
|
||||
|
||||
.#{$card-name} > h3.#{$card-section-name}.#{$card-section-double-padded-name} {
|
||||
padding: calc(3 * var(#{$universal-padding-var}));
|
||||
}
|
||||
|
||||
.#{$card-name} > .#{$card-section-name}.#{$card-section-double-padded-name} > p {
|
||||
margin: var(#{$universal-margin-var}) calc(var(#{$universal-margin-var}) / 2);
|
||||
}
|
||||
|
||||
.#{$card-name} + .#{$card-name} {
|
||||
margin-top: calc(5 * var(#{$universal-margin-var}));
|
||||
}
|
||||
|
||||
$form-back-color-var: '--frm-b-col';
|
||||
$form-fore-color-var: '--frm-f-col';
|
||||
$form-border-color-var: '--frm-br-col';
|
||||
$input-back-color-var: '--in-b-col';
|
||||
$input-fore-color-var: '--in-f-col';
|
||||
$input-border-color-var: '--in-br-col';
|
||||
$input-focus-color-var: '--in-fc-col';
|
||||
$input-invalid-color-var: '--in-inv-col';
|
||||
$button-back-color-var: '--btn-b-col';
|
||||
$button-hover-back-color-var: '--btn-h-b-col';
|
||||
$button-fore-color-var: '--btn-f-col';
|
||||
$button-border-color-var: '--btn-br-col';
|
||||
$button-hover-border-color-var: '--btn-h-br-col';
|
||||
$button-group-border-color-var: '--btn-grp-br-col';
|
||||
|
||||
|
||||
$_include-fluid-input-group: false;
|
||||
|
||||
@import 'input_control';
|
||||
|
||||
@ -57,40 +105,72 @@ $button-primary-hover-back-color:#1565c0; // Background color for primary bu
|
||||
$button-primary-fore-color: #f8f8f8; // Text color for primary button color variant.
|
||||
@include make-button-alt-color ($button-primary-name, $button-primary-back-color, $button-primary-hover-back-color, $button-primary-fore-color);
|
||||
|
||||
$button-secondary-name: 'secondary'; // Class name for secondary button color variant.
|
||||
$button-secondary-back-color: #d32f2f; // Background color for secondary button color variant.
|
||||
$button-secondary-hover-back-color:#c62828; // Background color for secondary button color variant (hover).
|
||||
$button-secondary-fore-color: #f8f8f8; // Text color for secondary button color variant.
|
||||
@include make-button-alt-color ($button-secondary-name, $button-secondary-back-color, $button-secondary-hover-back-color, $button-secondary-fore-color);
|
||||
|
||||
$button-tertiary-name: 'tertiary'; // Class name for tertiary button color variant.
|
||||
$button-tertiary-back-color: #308732; // Background color for tertiary button color variant.
|
||||
$button-tertiary-hover-back-color:#277529; // Background color for tertiary button color variant (hover).
|
||||
$button-tertiary-fore-color: #f8f8f8; // Text color for tertiary button color variant.
|
||||
@include make-button-alt-color ($button-tertiary-name, $button-tertiary-back-color, $button-tertiary-hover-back-color, $button-tertiary-fore-color);
|
||||
$header-fore-color-var: '--hd-f-col';
|
||||
$header-back-color-var: '--hd-b-col';
|
||||
$header-hover-back-color-var: '--hd-hv-b-col';
|
||||
$header-border-color-var: '--hd-br-col';
|
||||
$nav-fore-color-var: '--nv-f-col';
|
||||
$nav-back-color-var: '--nv-b-col';
|
||||
$nav-hover-back-color-var: '--nv-hv-b-col';
|
||||
$nav-border-color-var: '--nv-br-col';
|
||||
$nav-link-color-var: '--nv-ln-col';
|
||||
$footer-fore-color-var: '--ft-f-col';
|
||||
$footer-back-color-var: '--ft-b-col';
|
||||
$footer-border-color-var: '--ft-br-col';
|
||||
$footer-link-color-var: '--ft-ln-col';
|
||||
$drawer-back-color-var: '--dr-b-col';
|
||||
$drawer-border-color-var: '--dr-br-col';
|
||||
$drawer-hover-back-color-var: '--dr-hv-b-col';
|
||||
$drawer-close-color-var: '--dr-cl-col';
|
||||
|
||||
$button-inverse-name: 'inverse'; // Class name for inverse button color variant.
|
||||
$button-inverse-back-color: #212121; // Background color for inverse button color variant.
|
||||
$button-inverse-hover-back-color:#111; // Background color for inverse button color variant (hover).
|
||||
$button-inverse-fore-color: #f8f8f8; // Text color for inverse button color variant.
|
||||
@include make-button-alt-color ($button-inverse-name, $button-inverse-back-color, $button-inverse-hover-back-color, $button-inverse-fore-color);
|
||||
|
||||
$button-small-name: 'small'; // Class name, padding and margin for small button size variant.
|
||||
$button-small-padding: calc(0.5 * var(#{$universal-padding-var})) calc(0.75 * var(#{$universal-padding-var}));
|
||||
$button-small-margin: var(#{$universal-margin-var});
|
||||
@include make-button-alt-size ($button-small-name, $button-small-padding, $button-small-margin);
|
||||
|
||||
$button-large-name: 'large'; // Class name, padding and margin for large button size variant.
|
||||
$button-large-padding: calc(1.5 * var(#{$universal-padding-var})) calc(2 * var(#{$universal-padding-var}));
|
||||
$button-large-margin: var(#{$universal-margin-var});
|
||||
@include make-button-alt-size ($button-large-name, $button-large-padding, $button-large-margin);
|
||||
|
||||
$nav-sublink-depth: 1;
|
||||
|
||||
$_drawer-right: false;
|
||||
|
||||
@import 'navigation';
|
||||
|
||||
$mark-back-color: #424242;
|
||||
$mark-font-size: 0.5em;
|
||||
|
||||
$toast-back-color: #212121;
|
||||
|
||||
$mark-back-color-var: '--mrk-b-col';
|
||||
$mark-fore-color-var: '--mrk-f-col';
|
||||
$toast-back-color-var: '--tst-b-col';
|
||||
$toast-fore-color-var: '--tst-f-col';
|
||||
$tooltip-back-color-var: '--tltp-b-col';
|
||||
$tooltip-fore-color-var: '--tltp-f-col';
|
||||
$modal-overlay-color-var: '--mdl-ov-col';
|
||||
$modal-close-color-var: '--mdl-cl-col';
|
||||
$modal-close-hover-back-color-var: '--mdl-cl-h-col';
|
||||
$collapse-label-back-color-var: '--clps-lbl-b-col';
|
||||
$collapse-label-fore-color-var: '--clps-lbl-f-col';
|
||||
$collapse-label-hover-back-color-var: '--clps-lbl-h-b-col';
|
||||
$collapse-selected-label-back-color-var: '--clps-sel-lbl-b-col';
|
||||
$collapse-border-color-var: '--clps-br-col';
|
||||
$collapse-content-back-color-var: '--clps-cnt-b-col';
|
||||
$collapse-selected-label-border-color-var: '--clps-sel-lbl-br-col';
|
||||
|
||||
$_include-modal: false;
|
||||
$_include-tooltip: false;
|
||||
$_include-collapse: false;
|
||||
|
||||
@import 'contextual';
|
||||
|
||||
.#{$toast-name} {
|
||||
bottom: calc(var(#{$universal-margin-var}) / 2);
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
mark {
|
||||
position: relative;
|
||||
top: -0.25rem;
|
||||
left: 0.25rem;
|
||||
}
|
||||
|
||||
/*
|
||||
Custom elements for contextual background elements, toasts and tooltips.
|
||||
*/
|
||||
@ -108,25 +188,45 @@ $mark-tag-border-radius: 1em;
|
||||
@include make-mark-alt-size ($mark-tag-name, $mark-tag-padding, $mark-tag-border-radius);
|
||||
|
||||
// Website-specific styles
|
||||
|
||||
html, * { font-family: 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Helvetica, sans-serif; }
|
||||
code, pre, kbd, code *, pre *, kbd *, code[class*="language-"], pre[class*="language-"] {
|
||||
font-family: 'Inconsolata', Menlo, Consolas, monospace !important;
|
||||
}
|
||||
code, kbd { font-size: 1em; }
|
||||
pre code {
|
||||
padding: 0; // Should make the first line's left padding the same as all other lines and avoid that annoying little step.
|
||||
font-family: Menlo, Consolas, monospace !important;
|
||||
}
|
||||
code {
|
||||
transform: scale(1); /* Deals with the issue described in #243 */
|
||||
pre {
|
||||
border: 0.0625rem solid var(#{$secondary-border-color-var});
|
||||
border-radius: var(#{$universal-border-radius-var});
|
||||
}
|
||||
.group{
|
||||
position:relative;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 1em
|
||||
}
|
||||
.search{
|
||||
font-size: 0.875rem;
|
||||
margin-top: -0.1em;
|
||||
display:block;
|
||||
width:100%;
|
||||
border:none;
|
||||
border-bottom: $__1px solid var(#{$nav-link-color-var});
|
||||
}
|
||||
.search:focus{
|
||||
outline:none
|
||||
}
|
||||
label#search-label{
|
||||
color:var(#{$nav-link-color-var});
|
||||
font-size: 1.125rem;
|
||||
font-weight:400;
|
||||
position:absolute;
|
||||
left: 0.3125rem;
|
||||
top: 0.625rem;
|
||||
}
|
||||
.search:focus ~ label#search-label,.search:valid ~ label#search-label{
|
||||
top: -1.25rem;
|
||||
font-size: 0.875rem;
|
||||
color:var(#{$nav-link-color-var});
|
||||
}
|
||||
label#menu-toggle {
|
||||
width: 3.4375rem;
|
||||
}
|
||||
pre { font-size: 1rem; border: 0.0625rem solid var(--secondary-border-color); border-radius: var(--universal-border-radius);}
|
||||
.group{position:relative;margin-top:2em;margin-bottom:-1em}
|
||||
.search{font-size:14px;margin-top:-.1em;display:block;width:100%;border:none;border-bottom:1px solid var(--nav-link-color)}
|
||||
.search:focus{outline:none}
|
||||
label#search-label{color:var(--nav-link-color);font-size:18px;font-weight:400;position:absolute;left:5px;top:10px}
|
||||
.search:focus ~ label#search-label,.search:valid ~ label#search-label{top:-20px;font-size:14px;color:var(--nav-link-color)}
|
||||
label#menu-toggle { width: 3.4375rem;}
|
||||
|
||||
header h1.logo {
|
||||
margin-top: -0.8rem;
|
||||
@ -146,7 +246,6 @@ header #title {
|
||||
header h1 small {
|
||||
display:block;
|
||||
font-size: 0.875rem;
|
||||
font-style: italic;
|
||||
color: #888;
|
||||
margin-top: -0.8rem;
|
||||
@media screen and (max-width: 768px) { font-size: 0.75rem; }
|
||||
@ -161,34 +260,58 @@ label#menu-toggle {
|
||||
width: 3.4375rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 0.25rem 0.25rem 0 rgba(0, 0, 0, 0.125), 0 0.125rem 0.125rem -0.125rem rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
background: none !important;
|
||||
:root {
|
||||
#{$collapse-label-back-color-var}: $collapse-label-back-color;
|
||||
#{$collapse-label-fore-color-var}: $collapse-label-fore-color;
|
||||
#{$collapse-label-hover-back-color-var}: $collapse-label-hover-back-color;
|
||||
#{$collapse-selected-label-back-color-var}: $collapse-selected-label-back-color;
|
||||
#{$collapse-border-color-var}: $collapse-border-color;
|
||||
#{$collapse-content-back-color-var} : $collapse-content-back-color;
|
||||
#{$collapse-selected-label-border-color-var}: $collapse-selected-label-border-color;
|
||||
}
|
||||
|
||||
div.collapse {
|
||||
> label {
|
||||
border-top-left-radius: var(--universal-border-radius) !important;
|
||||
border-top-right-radius: var(--universal-border-radius) !important;
|
||||
}
|
||||
margin: 0;
|
||||
label.#{$collapse-name} {
|
||||
width: 100%;
|
||||
> div {
|
||||
padding: 0 !important;
|
||||
> pre {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
transition: background 0.3s;
|
||||
color: var(#{$collapse-label-fore-color-var});
|
||||
background: var(#{$collapse-label-back-color-var});
|
||||
border: $__1px solid var(#{$collapse-border-color-var});
|
||||
padding: calc(1.5 * var(#{$universal-padding-var}));
|
||||
border-radius: var(#{$universal-border-radius-var});
|
||||
&:hover, &:focus {
|
||||
background: var(#{$collapse-label-hover-back-color-var});
|
||||
}
|
||||
+ pre {
|
||||
box-sizing: border-box;
|
||||
height: 0;
|
||||
max-height: 1px;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
transition: max-height 0.3s;
|
||||
}
|
||||
&.toggled {
|
||||
background: var(#{$collapse-selected-label-back-color-var});
|
||||
border-bottom-color: var(#{$collapse-selected-label-border-color-var});
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
+ pre {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: $__1px solid var(#{$collapse-border-color-var});
|
||||
border-top: 0;
|
||||
padding: calc(2 * var(#{$universal-padding-var}));
|
||||
max-height: $collapse-content-max-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +319,138 @@ div.collapse {
|
||||
button.primary.clipboard-copy {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
> svg {
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #222;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.8;
|
||||
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
|
||||
-webkit-hypens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"] {
|
||||
padding: calc(2 * var(#{$universal-padding-var}));
|
||||
overflow: auto;
|
||||
margin: var(#{$universal-margin-var}) 0;
|
||||
}
|
||||
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #7a8490;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted,
|
||||
.token.function {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
|
||||
.token.number,
|
||||
.token.class-name {
|
||||
color: #832ed2;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #067e36;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.token.regex {
|
||||
color: #097cab;
|
||||
}
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
button.scroll-to-top {
|
||||
border-radius: 100%;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
box-sizing: border-box;
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 2rem;
|
||||
background: var(#{$back-color-var});
|
||||
box-shadow: 0 .25rem .25rem 0 rgba(0,0,0,0.125),0 .125rem .125rem -.125rem rgba(0,0,0,0.25);
|
||||
&:hover, &:focus {
|
||||
background: var(#{$secondary-back-color-var});
|
||||
}
|
||||
}
|
||||
|
||||
140
docs/prism.css
140
docs/prism.css
@ -1,140 +0,0 @@
|
||||
/* PrismJS 1.9.0
|
||||
http://prismjs.com/download.html?themes=prism&languages=clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #a67f59;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
2663
package-lock.json
generated
2663
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -1,34 +1,50 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"chalk": "^2.3.0",
|
||||
"fs-extra": "^4.0.2",
|
||||
"html-minifier": "^3.5.7",
|
||||
"jsdom": "^11.6.1",
|
||||
"markdown-it": "^8.4.0",
|
||||
"mini.css": "^2.3.7",
|
||||
"node-sass": "^4.7.2",
|
||||
"prettier": "^1.9.2",
|
||||
"prismjs": "^1.9.0",
|
||||
"rollup": "^0.53.2",
|
||||
"rollup-plugin-babel": "^3.0.3",
|
||||
"rollup-plugin-babel-minify": "^3.1.2",
|
||||
"semistandard": "^11.0.0",
|
||||
"chalk": "^2.3.0",
|
||||
"tap-spec": "^4.1.1",
|
||||
"tape": "^4.8.0"
|
||||
},
|
||||
"name": "30-seconds-of-code",
|
||||
"description": "A collection of useful JavaScript snippets.",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"version": "0.0.1",
|
||||
"main": "dist/_30s.js",
|
||||
"module": "dist/_30s.esm.js",
|
||||
"scripts": {
|
||||
"builder": "node ./scripts/build.js",
|
||||
"linter": "node ./scripts/lint.js",
|
||||
"tagger": "node ./scripts/tag.js",
|
||||
"webber": "node ./scripts/web.js",
|
||||
"tdd": "node ./scripts/tdd.js"
|
||||
"tester": "node ./scripts/tdd.js",
|
||||
"packager": "node ./scripts/module.js",
|
||||
"test": "tape test/**/*.test.js | tap-spec"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Chalarangelo/30-seconds-of-code.git"
|
||||
},
|
||||
"keywords": ["javascript", "snippets", "list"],
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"snippets",
|
||||
"list"
|
||||
],
|
||||
"author": "Chalarangelo (chalarangelo@gmail.com)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Chalarangelo/30-seconds-of-code/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Chalarangelo/30-seconds-of-code#readme"
|
||||
"homepage": "https://github.com/Chalarangelo/30-seconds-of-code#readme",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
103
scripts/build.js
103
scripts/build.js
@ -1,20 +1,78 @@
|
||||
/*
|
||||
This is the builder script that generates the README file.
|
||||
This is the builder script that generates the README and SNIPPETS_ARCHIVE files.
|
||||
Run using `npm run builder`.
|
||||
*/
|
||||
// Load modules
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
// Paths
|
||||
const SNIPPETS_PATH = './snippets';
|
||||
const SNIPPETS_ARCHIVE_PATH = './snippets_archive';
|
||||
const STATIC_PARTS_PATH = './static-parts';
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
if(isTravisCI() && /^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])) {
|
||||
console.log(`${chalk.green('NOBUILD')} README build terminated, parent commit is a Travis build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
if(isTravisCI() && (process.env['TRAVIS_EVENT_TYPE'] === 'cron' || process.env['TRAVIS_EVENT_TYPE'] === 'api')){
|
||||
console.log(`${chalk.green('ARCHIVE')} Cron job or custom build, building archive README!`);
|
||||
console.time('Builder');
|
||||
let snippets = {};
|
||||
// Synchronously read all snippets from snippets_archive folder and sort them as necessary (case-insensitive)
|
||||
try {
|
||||
const snippetFilenames = fs
|
||||
.readdirSync(SNIPPETS_ARCHIVE_PATH)
|
||||
.sort((a, b) => a.toLowerCase() - b.toLowerCase());
|
||||
// Store the data read from each snippet in the appropriate object
|
||||
for (const name of snippetFilenames.filter(s => s !== 'README.md')) {
|
||||
snippets[name] = fs.readFileSync(path.join(SNIPPETS_ARCHIVE_PATH, name), 'utf8');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
// Add the start static part
|
||||
let output = `
|
||||
|
||||
# Snippets Archive
|
||||
|
||||
These snippets, while useful and interesting, didn\'t quite make it into the repository due to either having very specific use-cases or being outdated. However we felt like they might still be useful to some readers, so here they are.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
`
|
||||
for(const snippet of Object.entries(snippets))
|
||||
output += `* [\`${snippet[0].slice(0,-3)}\`](#${snippet[0].toLowerCase().slice(0,-3)})\n`;
|
||||
output += '\n---\n';
|
||||
for(const snippet of Object.entries(snippets)){
|
||||
let data = snippet[1];
|
||||
data =
|
||||
data.slice(0, data.lastIndexOf('```js')) +
|
||||
'<details>\n<summary>Examples</summary>\n\n' +
|
||||
data.slice(data.lastIndexOf('```js'), data.lastIndexOf('```')) +
|
||||
data.slice(data.lastIndexOf('```')) +
|
||||
'\n</details>\n';
|
||||
output += `\n${data + '\n<br>[⬆ Back to top](#table-of-contents)\n\n'}`;
|
||||
}
|
||||
|
||||
// Write to the README file of the archive
|
||||
fs.writeFileSync(path.join(SNIPPETS_ARCHIVE_PATH,'README.md'), output);
|
||||
} catch (err) {
|
||||
console.log(`${chalk.red('ERROR!')} During README generation for snippets archive: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`${chalk.green('SUCCESS!')} README file generated for snippets archive!`);
|
||||
console.timeEnd('Builder');
|
||||
}
|
||||
const snippets = {};
|
||||
const emojis = {
|
||||
const EMOJIS = {
|
||||
adapter: '🔌',
|
||||
array: '📚',
|
||||
browser: '🖥️',
|
||||
browser: '🌐',
|
||||
date: '⏱️',
|
||||
function: '🎛️',
|
||||
logic: '🔮',
|
||||
@ -23,7 +81,8 @@ const emojis = {
|
||||
node: '📦',
|
||||
object: '🗃️',
|
||||
string: '📜',
|
||||
utility: '💎'
|
||||
type: '📃',
|
||||
utility: '🔧'
|
||||
};
|
||||
|
||||
let startPart = '',
|
||||
@ -38,7 +97,7 @@ const capitalize = (str, lowerRest = false) =>
|
||||
|
||||
console.time('Builder');
|
||||
|
||||
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
||||
// Synchronously read all snippets from snippets folder and sort them as necessary (case-insensitive)
|
||||
try {
|
||||
const snippetFilenames = fs
|
||||
.readdirSync(SNIPPETS_PATH)
|
||||
@ -68,7 +127,11 @@ try {
|
||||
.readFileSync('tag_database', 'utf8')
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(v => v.split(':').slice(0, 2))
|
||||
.map(v => {
|
||||
let data = v.split(':').slice(0, 2);
|
||||
data[1] = data[1].split(',').map(t => t.trim());
|
||||
return data;
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
||||
@ -80,7 +143,7 @@ try {
|
||||
const tags = [
|
||||
...new Set(
|
||||
Object.entries(tagDbData)
|
||||
.map(t => t[1])
|
||||
.map(t => t[1][0])
|
||||
.filter(v => v)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
)
|
||||
@ -96,16 +159,16 @@ try {
|
||||
|
||||
if (capitalizedTag === 'Uncategorized') {
|
||||
uncategorizedOutput += `### _${capitalizedTag}_\n\n<details>\n<summary>View contents</summary>\n\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)) {
|
||||
uncategorizedOutput += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
|
||||
uncategorizedOutput += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()}${taggedSnippet[1].includes('advanced')?'-':''})\n`;
|
||||
}
|
||||
uncategorizedOutput += '\n</details>\n\n';
|
||||
} else {
|
||||
output += `### ${
|
||||
emojis[tag] || ''
|
||||
EMOJIS[tag] || ''
|
||||
} ${capitalizedTag}\n\n<details>\n<summary>View contents</summary>\n\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)) {
|
||||
output += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
|
||||
output += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()}${taggedSnippet[1].includes('advanced')?'-':''})\n`;
|
||||
}
|
||||
output += '\n</details>\n\n';
|
||||
}
|
||||
@ -117,17 +180,23 @@ try {
|
||||
// Loop over tags and snippets to create the list of snippets
|
||||
for (const tag of tags) {
|
||||
const capitalizedTag = capitalize(tag, true);
|
||||
|
||||
// 
|
||||
if (capitalizedTag == 'Uncategorized') {
|
||||
uncategorizedOutput += `---\n ## _${capitalizedTag}_\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)) {
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
|
||||
uncategorizedOutput += `\n${snippets[taggedSnippet[0] + '.md'] +
|
||||
'\n<br>[⬆ back to top](#table-of-contents)\n\n'}`;
|
||||
}
|
||||
} else {
|
||||
output += `---\n ## ${emojis[tag] || ''} ${capitalizedTag}\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)) {
|
||||
output += `---\n ## ${EMOJIS[tag] || ''} ${capitalizedTag}\n`;
|
||||
for (const taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
|
||||
let data = snippets[taggedSnippet[0] + '.md'];
|
||||
// Add advanced tag
|
||||
if(taggedSnippet[1].includes('advanced')){
|
||||
data = data.split(/\r?\n/);
|
||||
data[0] = data[0] +' ';
|
||||
data = data.join('\n');
|
||||
}
|
||||
data =
|
||||
data.slice(0, data.lastIndexOf('```js')) +
|
||||
'<details>\n<summary>Examples</summary>\n\n' +
|
||||
|
||||
@ -9,7 +9,12 @@ const fs = require('fs-extra');
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
if(isTravisCI() && /^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])) {
|
||||
console.log(`${chalk.green('NOBUILD')} Linting terminated, parent commit is a Travis build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
const SNIPPETS_PATH = './snippets';
|
||||
const TEMP_PATH = './temp';
|
||||
|
||||
|
||||
80
scripts/module.js
Normal file
80
scripts/module.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Builds the `_30s` module.
|
||||
*/
|
||||
// Load modules
|
||||
const fs = require('fs-extra');
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
if(isTravisCI() && process.env['TRAVIS_EVENT_TYPE'] !== 'cron' && process.env['TRAVIS_EVENT_TYPE'] !== 'api') {
|
||||
console.log(`${chalk.green('NOBUILD')} Module build terminated, not a cron job or a custom build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
// Set variables for paths
|
||||
const SNIPPETS_PATH = './snippets';
|
||||
const TEMP_PATH = './temp';
|
||||
const IMPORTS = './imports.js';
|
||||
// Regex for selecting code blocks
|
||||
const codeRE = /```\s*js([\s\S]*?)```/;
|
||||
// Start the timer of the script
|
||||
console.time('Packager');
|
||||
// Load tag data from the database and snippets from their folder
|
||||
try {
|
||||
const tagDatabase = fs.readFileSync('tag_database', 'utf8');
|
||||
const snippets = fs.readdirSync(SNIPPETS_PATH);
|
||||
// Create `temp` folder if it doesn't already exist.
|
||||
if (!fs.existsSync(TEMP_PATH)) {
|
||||
fs.mkdirSync(TEMP_PATH);
|
||||
}
|
||||
// Write `imports.js`
|
||||
fs.writeFileSync(IMPORTS, '');
|
||||
let exportStr = 'export default {';
|
||||
// Read all snippets and store them appropriately
|
||||
for (const snippet of snippets) {
|
||||
const snippetData = fs.readFileSync(
|
||||
path.join(SNIPPETS_PATH, snippet),
|
||||
'utf8'
|
||||
);
|
||||
const snippetName = snippet.replace('.md', '');
|
||||
// Check if a snippet is Node-only
|
||||
const isNodeSnippet = tagDatabase
|
||||
.slice(tagDatabase.indexOf(snippetName) + snippetName.length + 1)
|
||||
.split('\n')[0]
|
||||
.includes('node');
|
||||
// Read `imports.js` and write the data
|
||||
const importData = fs.readFileSync(IMPORTS);
|
||||
fs.writeFileSync(
|
||||
IMPORTS,
|
||||
importData + `\nimport { ${snippetName} } from './temp/${snippetName}.js'`
|
||||
);
|
||||
exportStr += `${snippetName},`;
|
||||
// Find the code in each snippet
|
||||
const code = snippetData.match(codeRE)[1].replace('\n', '');
|
||||
// Store the data to be written
|
||||
const toWrite = isNodeSnippet
|
||||
? `${code
|
||||
.replace('const ' + snippetName, 'export const ' + snippetName)
|
||||
// Prevents errors from being thrown in browser environment
|
||||
.replace('require(', 'typeof require !== "undefined" && require(')}`
|
||||
: `export ${code}`;
|
||||
// Write data to the proper file
|
||||
fs.writeFileSync(`${TEMP_PATH}/${snippetName}.js`, toWrite);
|
||||
}
|
||||
// Write to the proper files and start the `rollup` script
|
||||
exportStr += '}';
|
||||
fs.appendFileSync(IMPORTS, `\n${exportStr}`);
|
||||
cp.execSync('node ./scripts/rollup.js');
|
||||
// Clean up temporary data
|
||||
fs.removeSync(TEMP_PATH);
|
||||
fs.unlink(IMPORTS);
|
||||
// Log a success message
|
||||
console.log(`${chalk.green('SUCCESS!')} Snippet module built!`);
|
||||
// Log the time taken
|
||||
console.timeEnd('Packager');
|
||||
} catch (err) {
|
||||
// Handle errors (hopefully not!)
|
||||
console.log(`${chalk.red('ERROR!')} During module creation: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
62
scripts/rollup.js
Normal file
62
scripts/rollup.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
Part of the process for building the `_30s` module.
|
||||
*/
|
||||
// Load modules
|
||||
const fs = require('fs-extra');
|
||||
const { rollup } = require('rollup');
|
||||
const babel = require('rollup-plugin-babel');
|
||||
const minify = require('rollup-plugin-babel-minify');
|
||||
// Set variables for paths
|
||||
const INPUT_FILE = './imports.js';
|
||||
const MODULE_NAME = '_30s';
|
||||
const DIST = './dist';
|
||||
// Create `dist` folder if not existing
|
||||
if (!fs.existsSync(DIST)) fs.mkdirSync(DIST);
|
||||
// Setup babel and minification
|
||||
const es5 = babel({ presets: [['env', { modules: false }]] });
|
||||
const min = minify({ comments: false });
|
||||
// Create the bundles
|
||||
(async () => {
|
||||
const bundle = await rollup({ input: INPUT_FILE });
|
||||
const bundleES5 = await rollup({ input: INPUT_FILE, plugins: [es5] });
|
||||
const bundleMin = await rollup({ input: INPUT_FILE, plugins: [min] });
|
||||
const bundleES5Min = await rollup({
|
||||
input: INPUT_FILE,
|
||||
plugins: [es5, min]
|
||||
});
|
||||
|
||||
// UMD ES2017
|
||||
await bundle.write({
|
||||
file: `${DIST}/${MODULE_NAME}.js`,
|
||||
name: MODULE_NAME,
|
||||
format: 'umd'
|
||||
});
|
||||
|
||||
// UMD ES2017 minified
|
||||
await bundleMin.write({
|
||||
file: `${DIST}/${MODULE_NAME}.min.js`,
|
||||
name: MODULE_NAME,
|
||||
format: 'umd'
|
||||
});
|
||||
|
||||
// UMD ES5
|
||||
await bundleES5.write({
|
||||
file: `${DIST}/${MODULE_NAME}.es5.js`,
|
||||
name: MODULE_NAME,
|
||||
format: 'umd'
|
||||
});
|
||||
|
||||
// UMD ES5 min
|
||||
await bundleES5Min.write({
|
||||
file: `${DIST}/${MODULE_NAME}.es5.min.js`,
|
||||
name: MODULE_NAME,
|
||||
format: 'umd'
|
||||
});
|
||||
|
||||
// ESM ES2017
|
||||
await bundle.write({
|
||||
file: `${DIST}/${MODULE_NAME}.esm.js`,
|
||||
name: MODULE_NAME,
|
||||
format: 'es'
|
||||
});
|
||||
})();
|
||||
@ -6,6 +6,12 @@
|
||||
const fs = require('fs-extra'),
|
||||
path = require('path'),
|
||||
chalk = require('chalk');
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
if(isTravisCI() && /^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])) {
|
||||
console.log(`${chalk.green('NOBUILD')} Tagging terminated, parent commit is a Travis build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
// Set variables for paths
|
||||
const snippetsPath = './snippets';
|
||||
// Set variables for script
|
||||
@ -44,12 +50,15 @@ try {
|
||||
.readFileSync('tag_database', 'utf8')
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(v => v.split(':').slice(0, 2))
|
||||
.map(v => {
|
||||
let data = v.split(':').slice(0, 2);
|
||||
data[1] = data[1].split(',').map(t => t.trim());
|
||||
return data;
|
||||
})
|
||||
);
|
||||
tagDbStats = Object.entries(tagDbData)
|
||||
.sort((a, b) => a[1].localeCompare(b[1]))
|
||||
.reduce((acc, val) => {
|
||||
acc.hasOwnProperty(val[1]) ? acc[val[1]]++ : (acc[val[1]] = 1);
|
||||
val[1].forEach(v => acc.hasOwnProperty(v) ? acc[v]++ : (acc[v] = 1));
|
||||
return acc;
|
||||
}, {});
|
||||
} catch (err) {
|
||||
@ -62,9 +71,9 @@ try {
|
||||
for (let snippet of Object.entries(snippets))
|
||||
if (
|
||||
tagDbData.hasOwnProperty(snippet[0].slice(0, -3)) &&
|
||||
tagDbData[snippet[0].slice(0, -3)].trim()
|
||||
tagDbData[snippet[0].slice(0, -3)].join(',').trim()
|
||||
)
|
||||
output += `${snippet[0].slice(0, -3)}:${tagDbData[snippet[0].slice(0, -3)].trim()}\n`;
|
||||
output += `${snippet[0].slice(0, -3)}:${tagDbData[snippet[0].slice(0, -3)].join(',').trim()}\n`;
|
||||
else {
|
||||
output += `${snippet[0].slice(0, -3)}:uncategorized\n`;
|
||||
missingTags++;
|
||||
@ -79,7 +88,7 @@ try {
|
||||
}
|
||||
// Log statistics for the tag_database file
|
||||
console.log(`\n${chalk.bgWhite(chalk.black('=== TAG STATS ==='))}`);
|
||||
for (let tagData of Object.entries(tagDbStats).filter(v => v[0] !== 'undefined'))
|
||||
for (let tagData of Object.entries(tagDbStats).filter(v => v[0] !== 'undefined').sort((a,b) => a[0].localeCompare(b[0])))
|
||||
console.log(`${chalk.green(tagData[0])}: ${tagData[1]} snippets`);
|
||||
console.log(
|
||||
`${chalk.blue("New untagged snippets (will be tagged as 'uncategorized'):")} ${missingTags}\n`
|
||||
|
||||
103
scripts/tdd.js
103
scripts/tdd.js
@ -1,50 +1,99 @@
|
||||
const fs = require('fs-extra');
|
||||
/*
|
||||
This is the tdd script that creates & updates your TDD environment .
|
||||
Run using `npm run tdd`.
|
||||
*/
|
||||
|
||||
const SNIPPETS_PATH = './snippets';
|
||||
// Load modules
|
||||
const fs = require('fs-extra'), path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const chalk = require('chalk');
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
if(isTravisCI() && process.env['TRAVIS_EVENT_TYPE'] !== 'cron' && process.env['TRAVIS_EVENT_TYPE'] !== 'api') {
|
||||
console.log(`${chalk.green('NOBUILD')} Testing terminated, not a cron job or a custom build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
// Declare paths
|
||||
const SNIPPETS_ACTIVE = './snippets';
|
||||
const SNIPPETS_ARCHIVE = './snippets_archive';
|
||||
const TEST_PATH = './test';
|
||||
|
||||
const snippetFiles = fs.readdirSync(SNIPPETS_PATH, 'utf8').map(fileName => fileName.slice(0, -3));
|
||||
// Array of snippet names
|
||||
const snippetFiles = [];
|
||||
|
||||
fs.removeSync(TEST_PATH);
|
||||
const snippetFilesActive = fs.readdirSync(SNIPPETS_ACTIVE, 'utf8').map(fileName => fileName.slice(0, -3));
|
||||
const snippetFilesArchive = fs.readdirSync(SNIPPETS_ARCHIVE, 'utf8')
|
||||
.filter(fileName => !fileName.includes('README')) // -> Filters out main README.md file in Archieve which isn't a snippet
|
||||
.map(fileName => fileName.slice(0, -3));
|
||||
|
||||
snippetFiles.push(...snippetFilesActive);
|
||||
snippetFiles.push(...snippetFilesArchive);
|
||||
|
||||
console.time('Tester');
|
||||
snippetFiles
|
||||
.map(fileName => {
|
||||
fs.ensureDirSync(`${TEST_PATH}/${fileName}`);
|
||||
// Check if fileName for snippet exist in test/ dir, if doesnt create
|
||||
fs.ensureDirSync(path.join(TEST_PATH,fileName));
|
||||
|
||||
// return fileName for later use
|
||||
return fileName;
|
||||
})
|
||||
.map(fileName => {
|
||||
const fileData = fs.readFileSync(`${SNIPPETS_PATH}/${fileName}.md`, 'utf8');
|
||||
const fileCode = fileData.slice(fileData.indexOf('```js'), fileData.lastIndexOf('```') + 3);
|
||||
const activeOrArchive = snippetFilesActive.includes(fileName) ? SNIPPETS_ACTIVE : SNIPPETS_ARCHIVE;
|
||||
// Grab snippetData
|
||||
const fileData = fs.readFileSync(path.join(activeOrArchive,`${fileName}.md`), 'utf8');
|
||||
// Grab snippet Code blocks
|
||||
const fileCode = fileData.slice(fileData.search(/```\s*js/i), fileData.lastIndexOf('```') + 3);
|
||||
// Split code based on code markers
|
||||
const blockMarkers = fileCode
|
||||
.split('\n')
|
||||
.map((line, lineIndex) => (line.slice(0, 3) === '```' ? lineIndex : '//CLEAR//'))
|
||||
.filter(x => !(x === '//CLEAR//'));
|
||||
.split('\n')
|
||||
.map((line, lineIndex) => (line.slice(0, 3) === '```' ? lineIndex : '//CLEAR//'))
|
||||
.filter(x => !(x === '//CLEAR//'));
|
||||
// Grab snippet function based on code markers
|
||||
const fileFunction = fileCode
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter((_, i) => blockMarkers[0] < i && i < blockMarkers[1]);
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter((_, i) => blockMarkers[0] < i && i < blockMarkers[1]);
|
||||
// Grab snippet example based on code markers
|
||||
const fileExample = fileCode
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter((_, i) => blockMarkers[2] < i && i < blockMarkers[3]);
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter((_, i) => blockMarkers[2] < i && i < blockMarkers[3]);
|
||||
|
||||
const exportFile = `module.exports = ${fileFunction.join('\n').slice(17)}`;
|
||||
// Export template for snippetName.js
|
||||
const exportFile = `${fileFunction.join('\n')}\nmodule.exports = ${fileName}`.trim();
|
||||
|
||||
// Export template for snippetName.test.js which generates a example test & other information
|
||||
const exportTest = [
|
||||
`const test = require('tape');`,
|
||||
`const ${fileName} = require('./${fileName}.js');`,
|
||||
`test('Testing ${fileName}', (t) => {`,
|
||||
`//For more information on all the methods supported by tape\n//Please go to https://github.com/substack/tape`,
|
||||
`//t.deepEqual(${fileName}(args..), 'Expected');`,
|
||||
`//t.equal(${fileName}(args..), 'Expected');`,
|
||||
`//t.false(${fileName}(args..), 'Expected');`,
|
||||
`//t.true(${fileName}(args..), 'Expected');`,
|
||||
`//t.throws(${fileName}(args..), 'Expected');`,
|
||||
`t.end();`,
|
||||
`\ntest('Testing ${fileName}', (t) => {`,
|
||||
` //For more information on all the methods supported by tape\n //Please go to https://github.com/substack/tape`,
|
||||
` t.true(typeof ${fileName} === 'function', '${fileName} is a Function');`,
|
||||
` //t.deepEqual(${fileName}(args..), 'Expected');`,
|
||||
` //t.equal(${fileName}(args..), 'Expected');`,
|
||||
` //t.false(${fileName}(args..), 'Expected');`,
|
||||
` //t.throws(${fileName}(args..), 'Expected');`,
|
||||
` t.end();`,
|
||||
`});`
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.js`, exportFile);
|
||||
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.test.js`, exportTest);
|
||||
// Write/Update exportFile which is snippetName.js in respective dir
|
||||
fs.writeFileSync(path.join(TEST_PATH,fileName,`${fileName}.js`), exportFile);
|
||||
|
||||
if ( !fs.existsSync(path.join(TEST_PATH,fileName,`${fileName}.test.js`)) ) {
|
||||
// if snippetName.test.js doesn't exist inrespective dir exportTest
|
||||
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.test.js`, exportTest);
|
||||
}
|
||||
|
||||
// return fileName for later use
|
||||
return fileName;
|
||||
});
|
||||
try {
|
||||
fs.writeFileSync(path.join(TEST_PATH,'testlog'),`Test log for: ${new Date().toString()}\n`);
|
||||
child_process.execSync(`npm test >> ${TEST_PATH}/testlog`);
|
||||
}
|
||||
catch (e) {
|
||||
fs.appendFileSync(path.join(TEST_PATH,'testlog'));
|
||||
}
|
||||
console.timeEnd('Tester');
|
||||
|
||||
102
scripts/web.js
102
scripts/web.js
@ -8,6 +8,25 @@ const fs = require('fs-extra'),
|
||||
chalk = require('chalk'),
|
||||
md = require('markdown-it')(),
|
||||
minify = require('html-minifier').minify;
|
||||
var Prism = require('prismjs');
|
||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
|
||||
const unescapeHTML = str =>
|
||||
str.replace(
|
||||
/&|<|>|'|"/g,
|
||||
tag =>
|
||||
({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
''': "'",
|
||||
'"': '"'
|
||||
}[tag] || tag)
|
||||
);
|
||||
if(isTravisCI() && /^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])) {
|
||||
console.log(`${chalk.green('NOBUILD')} index build terminated, parent commit is a Travis build!`);
|
||||
process.exit(0);
|
||||
}
|
||||
// Compile the mini.css framework and custom CSS styles, using `node-sass`.
|
||||
const sass = require('node-sass');
|
||||
sass.render(
|
||||
@ -42,7 +61,7 @@ const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
|
||||
const capitalize = (str, lowerRest = false) =>
|
||||
str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
|
||||
// Start the timer of the script
|
||||
console.time('Builder');
|
||||
console.time('Webber');
|
||||
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
||||
try {
|
||||
let snippetFilenames = fs.readdirSync(snippetsPath);
|
||||
@ -77,7 +96,11 @@ try {
|
||||
.readFileSync('tag_database', 'utf8')
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(v => v.split(':').slice(0, 2))
|
||||
.map(v => {
|
||||
let data = v.split(':').slice(0, 2);
|
||||
data[1] = data[1].split(',').map(t => t.trim());
|
||||
return data;
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
// Handle errors (hopefully not!)
|
||||
@ -90,7 +113,7 @@ try {
|
||||
output += `${startPart + '\n'}`;
|
||||
let uncategorizedOutput = '';
|
||||
// Loop over tags and snippets to create the table of contents
|
||||
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1][0]))]
|
||||
.filter(v => v)
|
||||
.sort((a, b) => a.localeCompare(b))) {
|
||||
if (capitalize(tag, true) == 'Uncategorized') {
|
||||
@ -101,12 +124,12 @@ try {
|
||||
.replace(/<p>/g, '')
|
||||
.replace(/<\/p>/g, '') +
|
||||
`</h3>`;
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
|
||||
uncategorizedOutput += md
|
||||
.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`)
|
||||
.replace(/<p>/g, '')
|
||||
.replace(/<\/p>/g, '')
|
||||
.replace(/<a/g, '<a class="sublink-1"');
|
||||
.replace(/<a/g, `<a class="sublink-1" tags="${taggedSnippet[1].join(',')}"`);
|
||||
uncategorizedOutput += '\n';
|
||||
} else {
|
||||
output +=
|
||||
@ -116,12 +139,12 @@ try {
|
||||
.replace(/<p>/g, '')
|
||||
.replace(/<\/p>/g, '') +
|
||||
`</h3>`;
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
|
||||
output += md
|
||||
.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`)
|
||||
.replace(/<p>/g, '')
|
||||
.replace(/<\/p>/g, '')
|
||||
.replace(/<a/g, '<a class="sublink-1"');
|
||||
.replace(/<a/g, `<a class="sublink-1" tags="${taggedSnippet[1].join(',')}"`);
|
||||
output += '\n';
|
||||
}
|
||||
}
|
||||
@ -130,38 +153,79 @@ try {
|
||||
output += `<a id="top"> </a>`;
|
||||
uncategorizedOutput = '';
|
||||
// Loop over tags and snippets to create the list of snippets
|
||||
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1][0]))]
|
||||
.filter(v => v)
|
||||
.sort((a, b) => a.localeCompare(b))) {
|
||||
if (capitalize(tag, true) == 'Uncategorized') {
|
||||
uncategorizedOutput += md
|
||||
.render(`## ${capitalize(tag, true)}\n`)
|
||||
.replace(/<h2>/g, '<h2 style="text-align:center;">');
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
|
||||
uncategorizedOutput +=
|
||||
'<div class="card fluid"><div class="section double-padded">' +
|
||||
'<div class="card fluid">' +
|
||||
md
|
||||
.render(`\n${snippets[taggedSnippet[0] + '.md']}`)
|
||||
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}"`)
|
||||
.replace(/<\/h3>/g, '</h3></div><div class="section double-padded">') +
|
||||
'</div></div><br/>';
|
||||
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}" class="section double-padded"`)
|
||||
.replace(/<\/h3>/g, `${taggedSnippet[1].includes('advanced')?'<mark class="tag">advanced</mark>':''}</h3>`)
|
||||
.replace(/<pre><code class="language-js">([^\0]*?)<\/code><\/pre>/gm, (match, p1) => `<pre class="language-js">${Prism.highlight(unescapeHTML(p1), Prism.languages.javascript)}</pre>`)
|
||||
.replace(/<\/pre>\s+<pre/g, '</pre><label class="collapse">Show examples</label><pre') +
|
||||
'<button class="primary clipboard-copy">📋 Copy to clipboard</button>' +
|
||||
'</div></div>';
|
||||
} else {
|
||||
output += md
|
||||
.render(`## ${capitalize(tag, true)}\n`)
|
||||
.replace(/<h2>/g, '<h2 style="text-align:center;">');
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
|
||||
output +=
|
||||
'<div class="card fluid"><div class="section double-padded">' +
|
||||
'<div class="card fluid">' +
|
||||
md
|
||||
.render(`\n${snippets[taggedSnippet[0] + '.md']}`)
|
||||
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}"`)
|
||||
.replace(/<\/h3>/g, '</h3></div><div class="section double-padded">') +
|
||||
'</div></div><br/>';
|
||||
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}" class="section double-padded"`)
|
||||
.replace(/<\/h3>/g, `${taggedSnippet[1].includes('advanced')?'<mark class="tag">advanced</mark>':''}</h3>`)
|
||||
.replace(/<\/h3>/g, '</h3><div class="section double-padded">')
|
||||
.replace(/<pre><code class="language-js">([^\0]*?)<\/code><\/pre>/gm, (match, p1) => `<pre class="language-js">${Prism.highlight(unescapeHTML(p1), Prism.languages.javascript)}</pre>`)
|
||||
.replace(/<\/pre>\s+<pre/g, '</pre><label class="collapse">Show examples</label><pre') +
|
||||
'<button class="primary clipboard-copy">📋 Copy to clipboard</button>' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
output += uncategorizedOutput;
|
||||
// Add the ending static part
|
||||
output += `\n${endPart + '\n'}`;
|
||||
// Optimize punctuation nodes
|
||||
let count = 0;
|
||||
do {
|
||||
const punctuationRegex = /<span class="token punctuation">([^\0<]*?)<\/span>([\n\r\s]*)<span class="token punctuation">([^\0]*?)<\/span>/gm;
|
||||
output = output.replace(punctuationRegex,
|
||||
(match, p1, p2, p3) => `<span class="token punctuation">${p1}${p2}${p3}</span>`
|
||||
);
|
||||
count = 0;
|
||||
while (punctuationRegex.exec(output) !== null) {
|
||||
++count;
|
||||
}
|
||||
} while (count > 0);
|
||||
// Optimize operator nodes
|
||||
do {
|
||||
const operatorRegex = /<span class="token operator">([^\0<]*?)<\/span>([\n\r\s]*)<span class="token operator">([^\0]*?)<\/span>/gm;
|
||||
output = output.replace(operatorRegex,
|
||||
(match, p1, p2, p3) => `<span class="token operator">${p1}${p2}${p3}</span>`
|
||||
);
|
||||
count = 0;
|
||||
while (operatorRegex.exec(output) !== null) {
|
||||
++count;
|
||||
}
|
||||
} while (count > 0);
|
||||
// Optimize keyword nodes
|
||||
do {
|
||||
const keyWordRegex = /<span class="token keyword">([^\0<]*?)<\/span>([\n\r\s]*)<span class="token keyword">([^\0]*?)<\/span>/gm;
|
||||
output = output.replace(keyWordRegex,
|
||||
(match, p1, p2, p3) => `<span class="token keyword">${p1}${p2}${p3}</span>`
|
||||
);
|
||||
count = 0;
|
||||
while (keyWordRegex.exec(output) !== null) {
|
||||
++count;
|
||||
}
|
||||
} while (count > 0);
|
||||
// Minify output
|
||||
output = minify(output, {
|
||||
collapseBooleanAttributes: true,
|
||||
@ -189,4 +253,4 @@ try {
|
||||
// Log a success message
|
||||
console.log(`${chalk.green('SUCCESS!')} index.html file generated!`);
|
||||
// Log the time taken
|
||||
console.timeEnd('Builder');
|
||||
console.timeEnd('Webber');
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Converts the values of RGB components to a color code.
|
||||
|
||||
Convert given RGB parameters to hexadecimal string using bitwise left-shift operator (`<<`) and `toString(16)`, then `padStart(6,'0')` to get a 6-digit hexadecimal value.
|
||||
Convert given RGB parameters to hexadecimal string using bitwise left-shift operator (`<<`) and `toString(16)`, then `String.padStart(6,'0')` to get a 6-digit hexadecimal value.
|
||||
|
||||
```js
|
||||
const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
|
||||
|
||||
21
snippets/URLJoin.md
Normal file
21
snippets/URLJoin.md
Normal file
@ -0,0 +1,21 @@
|
||||
### URLJoin
|
||||
|
||||
Joins all given URL segments together, then normalizes the resulting URL.
|
||||
|
||||
Use `String.join('/')` to combine URL segments, then a series of `String.replace()` calls with various regexps to normalize the resulting URL (remove double slashes, add proper slashes for protocol, remove slashes before parameters, combine parameters with `'&'` and normalize first parameter delimiter).
|
||||
|
||||
```js
|
||||
const URLJoin = (...args) =>
|
||||
args
|
||||
.join('/')
|
||||
.replace(/[\/]+/g, '/')
|
||||
.replace(/^(.+):\//, '$1://')
|
||||
.replace(/^file:/, 'file:/')
|
||||
.replace(/\/(\?|&|#[^!])/g, '$1')
|
||||
.replace(/\?/g, '&')
|
||||
.replace('&', '?');
|
||||
```
|
||||
|
||||
```js
|
||||
URLJoin('http://www.google.com', 'a', '/b/cd', '?foo=123', '?bar=foo'); // 'http://www.google.com/a/b/cd?foo=123&bar=foo'
|
||||
```
|
||||
@ -1,5 +1,7 @@
|
||||
### anagrams
|
||||
|
||||
⚠️ **WARNING**: This function's execution time increases exponentially with each character. Anything more than 8 to 10 characters will cause your browser to hang as it tries to solve all the different combinations.
|
||||
|
||||
Generates all anagrams of a string (contains duplicates).
|
||||
|
||||
Use recursion.
|
||||
|
||||
14
snippets/ary.md
Normal file
14
snippets/ary.md
Normal file
@ -0,0 +1,14 @@
|
||||
### ary
|
||||
|
||||
Creates a function that accepts up to `n` arguments, ignoring any additional arguments.
|
||||
|
||||
Call the provided function, `fn`, with up to `n` arguments, using `Array.slice(0,n)` and the spread operator (`...`).
|
||||
|
||||
```js
|
||||
const ary = (fn, n) => (...args) => fn(...args.slice(0, n));
|
||||
```
|
||||
|
||||
```js
|
||||
const firstTwoMax = ary(Math.max, 2);
|
||||
[[2, 6, 'a'], [8, 4, 6], [10]].map(x => firstTwoMax(...x)); // [6, 8, 10]
|
||||
```
|
||||
13
snippets/atob.md
Normal file
13
snippets/atob.md
Normal file
@ -0,0 +1,13 @@
|
||||
### atob
|
||||
|
||||
Decodes a string of data which has been encoded using base-64 encoding.
|
||||
|
||||
Create a `Buffer` for the given string with base-64 encoding and use `Buffer.toString('binary')` to return the decoded string.
|
||||
|
||||
```js
|
||||
const atob = str => new Buffer(str, 'base64').toString('binary');
|
||||
```
|
||||
|
||||
```js
|
||||
atob('Zm9vYmFy'); // 'foobar'
|
||||
```
|
||||
@ -1,17 +1,14 @@
|
||||
### average
|
||||
|
||||
Returns the average of an of two or more numbers/arrays.
|
||||
Returns the average of two or more numbers.
|
||||
|
||||
Use `Array.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
|
||||
|
||||
```js
|
||||
const average = (...arr) => {
|
||||
const nums = [].concat(...arr);
|
||||
return nums.reduce((acc, val) => acc + val, 0) / nums.length;
|
||||
};
|
||||
const average = (...nums) => [...nums].reduce((acc, val) => acc + val, 0) / nums.length;
|
||||
```
|
||||
|
||||
```js
|
||||
average([1, 2, 3]); // 2
|
||||
average(...[1, 2, 3]); // 2
|
||||
average(1, 2, 3); // 2
|
||||
```
|
||||
|
||||
16
snippets/averageBy.md
Normal file
16
snippets/averageBy.md
Normal file
@ -0,0 +1,16 @@
|
||||
### averageBy
|
||||
|
||||
Returns the average of an array, after mapping each element to a value using the provided function.
|
||||
|
||||
Use `Array.map()` to map each element to the value returned by `fn`, `Array.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
|
||||
|
||||
```js
|
||||
const averageBy = (arr, fn) =>
|
||||
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
|
||||
arr.length;
|
||||
```
|
||||
|
||||
```js
|
||||
averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
|
||||
averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5
|
||||
```
|
||||
22
snippets/bind.md
Normal file
22
snippets/bind.md
Normal file
@ -0,0 +1,22 @@
|
||||
### bind
|
||||
|
||||
Creates a function that invokes `fn` with a given context, optionally adding any additional supplied parameters to the beginning of the arguments.
|
||||
|
||||
Return a `function` that uses `Function.apply()` to apply the given `context` to `fn`.
|
||||
Use `Array.concat()` to prepend any additional supplied parameters to the arguments.
|
||||
|
||||
```js
|
||||
const bind = (fn, context, ...args) =>
|
||||
function() {
|
||||
return fn.apply(context, args.concat(...arguments));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
function greet(greeting, punctuation) {
|
||||
return greeting + ' ' + this.user + punctuation;
|
||||
}
|
||||
const freddy = { user: 'fred' };
|
||||
const freddyBound = bind(greet, freddy);
|
||||
console.log(freddyBound('hi', '!')); // 'hi fred!'
|
||||
```
|
||||
26
snippets/bindAll.md
Normal file
26
snippets/bindAll.md
Normal file
@ -0,0 +1,26 @@
|
||||
### bindAll
|
||||
|
||||
Explain briefly what the snippet does.
|
||||
|
||||
Use `Array.forEach()` to return a `function` that uses `Function.apply()` to apply the given context (`obj`) to `fn` for each function specified.
|
||||
|
||||
```js
|
||||
const bindAll = (obj, ...fns) =>
|
||||
fns.forEach(
|
||||
fn =>
|
||||
(obj[fn] = function() {
|
||||
return fn.apply(obj);
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
```js
|
||||
var view = {
|
||||
label: 'docs',
|
||||
click: function() {
|
||||
console.log('clicked ' + this.label);
|
||||
}
|
||||
};
|
||||
bindAll(view, 'click');
|
||||
jQuery(element).on('click', view.click); // Logs 'clicked docs' when clicked.
|
||||
```
|
||||
24
snippets/bindKey.md
Normal file
24
snippets/bindKey.md
Normal file
@ -0,0 +1,24 @@
|
||||
### bindKey
|
||||
|
||||
Creates a function that invokes the method at a given key of an object, optionally adding any additional supplied parameters to the beginning of the arguments.
|
||||
|
||||
Return a `function` that uses `Function.apply()` to bind `context[fn]` to `context`.
|
||||
Use `Array.concat()` to prepend any additional supplied parameters to the arguments.
|
||||
|
||||
```js
|
||||
const bindKey = (context, fn, ...args) =>
|
||||
function() {
|
||||
return context[fn].apply(context, args.concat(...arguments));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
const freddy = {
|
||||
user: 'fred',
|
||||
greet: function(greeting, punctuation) {
|
||||
return greeting + ' ' + this.user + punctuation;
|
||||
}
|
||||
};
|
||||
const freddyBound = bindKey(freddy, 'greet');
|
||||
console.log(freddyBound('hi', '!')); // 'hi fred!'
|
||||
```
|
||||
13
snippets/btoa.md
Normal file
13
snippets/btoa.md
Normal file
@ -0,0 +1,13 @@
|
||||
### btoa
|
||||
|
||||
Creates a base-64 encoded ASCII string from a String object in which each character in the string is treated as a byte of binary data.
|
||||
|
||||
Create a `Buffer` for the given string with binary encoding and use `Buffer.toString('base64')` to return the encoded string.
|
||||
|
||||
```js
|
||||
const btoa = str => new Buffer(str, 'binary').toString('base64');
|
||||
```
|
||||
|
||||
```js
|
||||
btoa('foobar'); // 'Zm9vYmFy'
|
||||
```
|
||||
@ -1,6 +1,6 @@
|
||||
### byteSize
|
||||
|
||||
Returns the length of string.
|
||||
Returns the length of a string in bytes.
|
||||
|
||||
Convert a given string to a [`Blob` Object](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and find its `size`.
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
### Capitalize
|
||||
### capitalize
|
||||
|
||||
Capitalizes the first letter of a string.
|
||||
|
||||
Use destructuring and `toUpperCase()` to capitalize first letter, `...rest` to get array of characters after first letter and then `Array.join('')` to make it a string again.
|
||||
Use array destructuring and `String.toUpperCase()` to capitalize first letter, `...rest` to get array of characters after first letter and then `Array.join('')` to make it a string again.
|
||||
Omit the `lowerRest` parameter to keep the rest of the string intact, or set it to `true` to convert to lowercase.
|
||||
|
||||
```js
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Capitalizes the first letter of every word in a string.
|
||||
|
||||
Use `replace()` to match the first character of each word and `toUpperCase()` to capitalize it.
|
||||
Use `String.replace()` to match the first character of each word and `String.toUpperCase()` to capitalize it.
|
||||
|
||||
```js
|
||||
const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());
|
||||
|
||||
14
snippets/castArray.md
Normal file
14
snippets/castArray.md
Normal file
@ -0,0 +1,14 @@
|
||||
### castArray
|
||||
|
||||
Casts the provided value as an array if it's not one.
|
||||
|
||||
Use `Array.isArray()` to determine if `val` is an array and return it as-is or encapsulated in an array accordingly.
|
||||
|
||||
```js
|
||||
const castArray = val => (Array.isArray(val) ? val : [val]);
|
||||
```
|
||||
|
||||
```js
|
||||
castArray('foo'); // ['foo']
|
||||
castArray([1]); // [1]
|
||||
```
|
||||
@ -20,10 +20,6 @@ chainAsync([
|
||||
},
|
||||
next => {
|
||||
console.log('1 second');
|
||||
setTimeout(next, 1000);
|
||||
},
|
||||
next => {
|
||||
console.log('2 seconds');
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
@ -12,5 +12,4 @@ const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.
|
||||
```js
|
||||
clampNumber(2, 3, 5); // 3
|
||||
clampNumber(1, -1, -5); // -1
|
||||
clampNumber(3, 2, 4); // 3
|
||||
```
|
||||
|
||||
14
snippets/cloneRegExp.md
Normal file
14
snippets/cloneRegExp.md
Normal file
@ -0,0 +1,14 @@
|
||||
### cloneRegExp
|
||||
|
||||
Clones a regular expression.
|
||||
|
||||
Use `new RegExp()`, `RegExp.source` and `RegExp.flags` to clone the given regular expression.
|
||||
|
||||
```js
|
||||
const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags);
|
||||
```
|
||||
|
||||
```js
|
||||
const regExp = /lorem ipsum/gi;
|
||||
const regExp2 = cloneRegExp(regExp); // /lorem ipsum/gi
|
||||
```
|
||||
33
snippets/colorize.md
Normal file
33
snippets/colorize.md
Normal file
@ -0,0 +1,33 @@
|
||||
### colorize
|
||||
|
||||
Add special characters to text to print in color in the console (combined with `console.log()`).
|
||||
|
||||
Use template literals and special characters to add the appropriate color code to the string output.
|
||||
For background colors, add a special character that resets the background color at the end of the string.
|
||||
|
||||
```js
|
||||
const colorize = (...args) => ({
|
||||
black: `\x1b[30m${args.join(' ')}`,
|
||||
red: `\x1b[31m${args.join(' ')}`,
|
||||
green: `\x1b[32m${args.join(' ')}`,
|
||||
yellow: `\x1b[33m${args.join(' ')}`,
|
||||
blue: `\x1b[34m${args.join(' ')}`,
|
||||
magenta: `\x1b[35m${args.join(' ')}`,
|
||||
cyan: `\x1b[36m${args.join(' ')}`,
|
||||
white: `\x1b[37m${args.join(' ')}`,
|
||||
bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`,
|
||||
bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`,
|
||||
bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`,
|
||||
bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`,
|
||||
bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`,
|
||||
bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`,
|
||||
bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`,
|
||||
bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m`
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
console.log(colorize('foo').red); // 'foo' (red letters)
|
||||
console.log(colorize('foo', 'bar').bgBlue); // 'foo bar' (blue background)
|
||||
console.log(colorize(colorize('foo').yellow, colorize('foo').green).bgWhite); // 'foo bar' (first word in yellow letters, second word in green letters, white background for both)
|
||||
```
|
||||
17
snippets/composeRight.md
Normal file
17
snippets/composeRight.md
Normal file
@ -0,0 +1,17 @@
|
||||
### composeRight
|
||||
|
||||
Performs left-to-right function composition.
|
||||
|
||||
Use `Array.reduce()` to perform left-to-right function composition.
|
||||
The first (leftmost) function can accept one or more arguments; the remaining functions must be unary.
|
||||
|
||||
```js
|
||||
const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
|
||||
```
|
||||
|
||||
```js
|
||||
const add = (x, y) => x + y;
|
||||
const square = x => x * x;
|
||||
const addAndSquare = composeRight(add, square);
|
||||
addAndSquare(1, 2); // 9
|
||||
```
|
||||
33
snippets/copyToClipboard.md
Normal file
33
snippets/copyToClipboard.md
Normal file
@ -0,0 +1,33 @@
|
||||
### copyToClipboard
|
||||
|
||||
Copy a string to the clipboard. Only works as a result of user action (i.e. inside a `click` event listener).
|
||||
|
||||
Create a new `<textarea>` element, fill it with the supplied data and add it to the HTML document.
|
||||
Use `Selection.getRangeAt()`to store the selected range (if any).
|
||||
Use `document.execCommand('copy')` to copy to the clipboard.
|
||||
Remove the `<textarea>` element from the HTML document.
|
||||
Finally, use `Selection().addRange()` to recover the original selected range (if any).
|
||||
|
||||
```js
|
||||
const copyToClipboard = str => {
|
||||
const el = document.createElement('textarea');
|
||||
el.value = str;
|
||||
el.setAttribute('readonly', '');
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = '-9999px';
|
||||
document.body.appendChild(el);
|
||||
const selected =
|
||||
document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
if (selected) {
|
||||
document.getSelection().removeAllRanges();
|
||||
document.getSelection().addRange(selected);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' copied to clipboard.
|
||||
```
|
||||
19
snippets/countBy.md
Normal file
19
snippets/countBy.md
Normal file
@ -0,0 +1,19 @@
|
||||
### countBy
|
||||
|
||||
Groups the elements of an array based on the given function and returns the count of elements in each group.
|
||||
|
||||
Use `Array.map()` to map the values of an array to a function or property name.
|
||||
Use `Array.reduce()` to create an object, where the keys are produced from the mapped results.
|
||||
|
||||
```js
|
||||
const countBy = (arr, fn) =>
|
||||
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
|
||||
acc[val] = (acc[val] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
```
|
||||
|
||||
```js
|
||||
countBy([6.1, 4.2, 6.3], Math.floor); // {4: 1, 6: 2}
|
||||
countBy(['one', 'two', 'three'], 'length'); // {3: 2, 5: 1}
|
||||
```
|
||||
@ -5,7 +5,7 @@ Counts the occurrences of a value in an array.
|
||||
Use `Array.reduce()` to increment a counter each time you encounter the specific value inside the array.
|
||||
|
||||
```js
|
||||
const countOccurrences = (arr, value) => arr.reduce((a, v) => (v === value ? a + 1 : a + 0), 0);
|
||||
const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a + 0), 0);
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
25
snippets/createElement.md
Normal file
25
snippets/createElement.md
Normal file
@ -0,0 +1,25 @@
|
||||
### createElement
|
||||
|
||||
Creates an element from a string (without appending it to the document).
|
||||
If the given string contains multiple elements, only the first one will be returned.
|
||||
|
||||
Use `document.createElement()` to create a new element.
|
||||
Set its `innerHTML` to the string supplied as the argument.
|
||||
Use `ParentNode.firstElementChild` to return the element version of the string.
|
||||
|
||||
```js
|
||||
const createElement = str => {
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = str;
|
||||
return el.firstElementChild;
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
const el = createElement(
|
||||
`<div class="container">
|
||||
<p>Hello!</p>
|
||||
</div>`
|
||||
);
|
||||
console.log(el.className); // 'container'
|
||||
```
|
||||
45
snippets/createEventHub.md
Normal file
45
snippets/createEventHub.md
Normal file
@ -0,0 +1,45 @@
|
||||
### createEventHub
|
||||
|
||||
Creates a pub/sub ([publish–subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)) event hub with `emit`, `on`, and `off` methods.
|
||||
|
||||
Use `Object.create(null)` to create an empty `hub` object that does not inherit properties from `Object.prototype`.
|
||||
For `emit`, resolve the array of handlers based on the `event` argument and then run each one with `Array.forEach()` by passing in the data as an argument.
|
||||
For `on`, create an array for the event if it does not yet exist, then use `Array.push()` to add the handler
|
||||
to the array.
|
||||
For `off`, use `Array.findIndex()` to find the index of the handler in the event array and remove it using `Array.splice()`.
|
||||
|
||||
```js
|
||||
const createEventHub = () => ({
|
||||
hub: Object.create(null),
|
||||
emit(event, data) {
|
||||
(this.hub[event] || []).forEach(handler => handler(data));
|
||||
},
|
||||
on(event, handler) {
|
||||
if (!this.hub[event]) this.hub[event] = [];
|
||||
this.hub[event].push(handler);
|
||||
},
|
||||
off(event, handler) {
|
||||
const i = (this.hub[event] || []).findIndex(h => h === handler);
|
||||
if (i > -1) this.hub[event].splice(i, 1);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
const handler = data => console.log(data);
|
||||
const hub = createEventHub();
|
||||
let increment = 0;
|
||||
|
||||
// Subscribe: listen for different types of events
|
||||
hub.on('message', handler);
|
||||
hub.on('message', () => console.log('Message event fired'));
|
||||
hub.on('increment', () => increment++);
|
||||
|
||||
// Publish: emit events to invoke all handlers subscribed to them, passing the data to them as an argument
|
||||
hub.emit('message', 'hello world'); // logs 'hello world' and 'Message event fired'
|
||||
hub.emit('message', { hello: 'world' }); // logs the object and 'Message event fired'
|
||||
hub.emit('increment'); // `increment` variable is now 1
|
||||
|
||||
// Unsubscribe: stop a specific handler from listening to the 'message' event
|
||||
hub.off('message', handler);
|
||||
```
|
||||
16
snippets/decapitalize.md
Normal file
16
snippets/decapitalize.md
Normal file
@ -0,0 +1,16 @@
|
||||
### decapitalize
|
||||
|
||||
Decapitalizes the first letter of a string.
|
||||
|
||||
Use array destructuring and `String.toLowerCase()` to decapitalize first letter, `...rest` to get array of characters after first letter and then `Array.join('')` to make it a string again.
|
||||
Omit the `upperRest` parameter to keep the rest of the string intact, or set it to `true` to convert to uppercase.
|
||||
|
||||
```js
|
||||
const decapitalize = ([first, ...rest], upperRest = false) =>
|
||||
first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join(''));
|
||||
```
|
||||
|
||||
```js
|
||||
decapitalize('FooBar'); // 'fooBar'
|
||||
decapitalize('FooBar', true); // 'fOOBAR'
|
||||
```
|
||||
22
snippets/deepClone.md
Normal file
22
snippets/deepClone.md
Normal file
@ -0,0 +1,22 @@
|
||||
### deepClone
|
||||
|
||||
Creates a deep clone of an object.
|
||||
|
||||
Use recursion.
|
||||
Use `Object.assign()` and an empty object (`{}`) to create a shallow clone of the original.
|
||||
Use `Object.keys()` and `Array.forEach()` to determine which key-value pairs need to be deep cloned.
|
||||
|
||||
```js
|
||||
const deepClone = obj => {
|
||||
let clone = Object.assign({}, obj);
|
||||
Object.keys(clone).forEach(
|
||||
key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
|
||||
);
|
||||
return clone;
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
const a = { foo: 'bar', obj: { a: 1, b: 2 } };
|
||||
const b = deepClone(a); // a !== b, a.obj !== b.obj
|
||||
```
|
||||
13
snippets/defaults.md
Normal file
13
snippets/defaults.md
Normal file
@ -0,0 +1,13 @@
|
||||
### defaults
|
||||
|
||||
Assigns default values for all properties in an object that are `undefined`.
|
||||
|
||||
Use `Object.assign()` to create a new empty object and copy the original one to maintain key order, use `Array.reverse()` and the spread operator `...` to combine the default values from left to right, finally use `obj` again to overwrite properties that originally had a value.
|
||||
|
||||
```js
|
||||
const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj);
|
||||
```
|
||||
|
||||
```js
|
||||
defaults({ a: 1 }, { b: 2 }, { b: 6 }, { a: 3 }); // { a: 1, b: 2 }
|
||||
```
|
||||
19
snippets/defer.md
Normal file
19
snippets/defer.md
Normal file
@ -0,0 +1,19 @@
|
||||
### defer
|
||||
|
||||
Defers invoking a function until the current call stack has cleared.
|
||||
|
||||
Use `setTimeout()` with a timeout of 1ms to add a new event to the browser event queue and allow the rendering engine to complete its work. Use the spread (`...`) operator to supply the function with an arbitrary number of arguments.
|
||||
|
||||
```js
|
||||
const defer = (fn, ...args) => setTimeout(fn, 1, ...args);
|
||||
```
|
||||
|
||||
```js
|
||||
// Example A:
|
||||
defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'
|
||||
|
||||
// Example B:
|
||||
document.querySelector('#someElement').innerHTML = 'Hello';
|
||||
longRunningFunction(); //Browser will not update the HTML until this has finished
|
||||
defer(longRunningFunction); // Browser will update the HTML then run the function
|
||||
```
|
||||
20
snippets/delay.md
Normal file
20
snippets/delay.md
Normal file
@ -0,0 +1,20 @@
|
||||
### delay
|
||||
|
||||
Invokes the provided function after `wait` milliseconds.
|
||||
|
||||
Use `setTimeout()` to delay execution of `fn`.
|
||||
Use the spread (`...`) operator to supply the function with an arbitrary number of arguments.
|
||||
|
||||
```js
|
||||
const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args);
|
||||
```
|
||||
|
||||
```js
|
||||
delay(
|
||||
function(text) {
|
||||
console.log(text);
|
||||
},
|
||||
1000,
|
||||
'later'
|
||||
); // Logs 'later' after one second.
|
||||
```
|
||||
@ -12,6 +12,5 @@ const detectDeviceType = () =>
|
||||
```
|
||||
|
||||
```js
|
||||
detectDeviceType(); // "Mobile"
|
||||
detectDeviceType(); // "Desktop"
|
||||
detectDeviceType(); // "Mobile" or "Desktop"
|
||||
```
|
||||
|
||||
17
snippets/differenceBy.md
Normal file
17
snippets/differenceBy.md
Normal file
@ -0,0 +1,17 @@
|
||||
### differenceBy
|
||||
|
||||
Returns the difference between two arrays, after applying the provided function to each array element of both.
|
||||
|
||||
Create a `Set` by applying `fn` to each element in `b`, then use `Array.filter()` in combination with `fn` on `a` to only keep values not contained in the previously created set.
|
||||
|
||||
```js
|
||||
const differenceBy = (a, b, fn) => {
|
||||
const s = new Set(b.map(v => fn(v)));
|
||||
return a.filter(x => !s.has(fn(x)));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
|
||||
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]
|
||||
```
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
Filters out all values from an array for which the comparator function does not return `true`.
|
||||
|
||||
Use `Array.filter()` and `Array.find()` to find the appropriate values.
|
||||
Use `Array.filter()` and `Array.findIndex()` to find the appropriate values.
|
||||
|
||||
```js
|
||||
const differenceWith = (arr, val, comp) => arr.filter(a => !val.find(b => comp(a, b)));
|
||||
const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1);
|
||||
```
|
||||
|
||||
```js
|
||||
differenceWith([1, 1.2, 1.5, 3], [1.9, 3], (a, b) => Math.round(a) == Math.round(b)); // [1, 1.2]
|
||||
differenceWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0], (a, b) => Math.round(a) === Math.round(b)); // [1, 1.2]
|
||||
```
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
|
||||
Converts a number to an array of digits.
|
||||
|
||||
Convert the number to a string, using spread operators in ES6(`[...string]`) build an array.
|
||||
Convert the number to a string, using the spread operator (`...`) to build an array.
|
||||
Use `Array.map()` and `parseInt()` to transform each value to an integer.
|
||||
|
||||
```js
|
||||
const digitize = n => [...('' + n)].map(i => parseInt(i));
|
||||
const digitize = n => [...`${n}`].map(i => parseInt(i));
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
### distinctValuesOfArray
|
||||
|
||||
Returns all the distinct values of an array.
|
||||
|
||||
Use ES6 `Set` and the `...rest` operator to discard all duplicated values.
|
||||
|
||||
```js
|
||||
const distinctValuesOfArray = arr => [...new Set(arr)];
|
||||
```
|
||||
|
||||
```js
|
||||
distinctValuesOfArray([1, 2, 2, 3, 4, 4, 5]); // [1,2,3,4,5]
|
||||
```
|
||||
15
snippets/drop.md
Normal file
15
snippets/drop.md
Normal file
@ -0,0 +1,15 @@
|
||||
### drop
|
||||
|
||||
Returns a new array with `n` elements removed from the left.
|
||||
|
||||
Use `Array.slice()` to slice the remove the specified number of elements from the left.
|
||||
|
||||
```js
|
||||
const drop = (arr, n = 1) => arr.slice(n);
|
||||
```
|
||||
|
||||
```js
|
||||
drop([1, 2, 3]); // [2,3]
|
||||
drop([1, 2, 3], 2); // [3]
|
||||
drop([1, 2, 3], 42); // []
|
||||
```
|
||||
17
snippets/dropRightWhile.md
Normal file
17
snippets/dropRightWhile.md
Normal file
@ -0,0 +1,17 @@
|
||||
### dropRightWhile
|
||||
|
||||
Removes elements from the end of an array until the passed function returns `true`. Returns the remaining elements in the array.
|
||||
|
||||
Loop through the array, using `Array.slice()` to drop the last element of the array until the returned value from the function is `true`.
|
||||
Returns the remaining elements.
|
||||
|
||||
```js
|
||||
const dropRightWhile = (arr, func) => {
|
||||
while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1);
|
||||
return arr;
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
dropRightWhile([1, 2, 3, 4], n => n < 3); // [1, 2]
|
||||
```
|
||||
@ -1,4 +1,4 @@
|
||||
### dropElements
|
||||
### dropWhile
|
||||
|
||||
Removes elements in an array until the passed function returns `true`. Returns the remaining elements in the array.
|
||||
|
||||
@ -6,12 +6,12 @@ Loop through the array, using `Array.slice()` to drop the first element of the a
|
||||
Returns the remaining elements.
|
||||
|
||||
```js
|
||||
const dropElements = (arr, func) => {
|
||||
const dropWhile = (arr, func) => {
|
||||
while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
|
||||
return arr;
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
dropElements([1, 2, 3, 4], n => n >= 3); // [3,4]
|
||||
dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]
|
||||
```
|
||||
@ -20,6 +20,6 @@ const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
|
||||
|
||||
```js
|
||||
// e.g. 100x100 viewport and a 10x10px element at position {top: -1, left: 0, bottom: 9, right: 10}
|
||||
elementIsVisibleInViewport(el); // false // (not fully visible)
|
||||
elementIsVisibleInViewport(el, true); // true // (partially visible)
|
||||
elementIsVisibleInViewport(el); // false - (not fully visible)
|
||||
elementIsVisibleInViewport(el, true); // true - (partially visible)
|
||||
```
|
||||
|
||||
44
snippets/elo.md
Normal file
44
snippets/elo.md
Normal file
@ -0,0 +1,44 @@
|
||||
### elo
|
||||
|
||||
Computes the new ratings between two or more opponents using the [Elo rating system](https://en.wikipedia.org/wiki/Elo_rating_system). It takes an array
|
||||
of pre-ratings and returns an array containing post-ratings.
|
||||
The array should be ordered from best performer to worst performer (winner -> loser).
|
||||
|
||||
Use the exponent `**` operator and math operators to compute the expected score (chance of winning).
|
||||
of each opponent and compute the new rating for each.
|
||||
Loop through the ratings, using each permutation to compute the post-Elo rating for each player in a pairwise fashion.
|
||||
Omit the second argument to use the default `kFactor` of 32.
|
||||
|
||||
```js
|
||||
const elo = ([...ratings], kFactor = 32, selfRating) => {
|
||||
const [a, b] = ratings;
|
||||
const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
|
||||
const newRating = (rating, i) =>
|
||||
(selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
|
||||
if (ratings.length === 2) {
|
||||
return [newRating(a, 1), newRating(b, 0)];
|
||||
} else {
|
||||
for (let i = 0; i < ratings.length; i++) {
|
||||
let j = i;
|
||||
while (j < ratings.length - 1) {
|
||||
[ratings[i], ratings[j + 1]] = elo([ratings[i], ratings[j + 1]], kFactor);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ratings;
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
// Standard 1v1s
|
||||
elo([1200, 1200]); // [1216, 1184]
|
||||
elo([1200, 1200], 64); // [1232, 1168]
|
||||
// 4 player FFA, all same rank
|
||||
elo([1200, 1200, 1200, 1200]).map(Math.round); // [1246, 1215, 1185, 1154]
|
||||
/*
|
||||
For teams, each rating can adjusted based on own team's average rating vs.
|
||||
average rating of opposing team, with the score being added to their
|
||||
own individual rating by supplying it as the third argument.
|
||||
*/
|
||||
```
|
||||
24
snippets/equals.md
Normal file
24
snippets/equals.md
Normal file
@ -0,0 +1,24 @@
|
||||
### equals
|
||||
|
||||
Performs a deep comparison between two values to determine if they are equivalent.
|
||||
|
||||
Check if the two values are identical, if they are both `Date` objects with the same time, using `Date.getTime()` or if they are both non-object values with an equivalent value (strict comparison).
|
||||
Check if only one value is `null` or `undefined` or if their prototypes differ.
|
||||
If none of the above conditions are met, use `Object.keys()` to check if both values have the same number of keys, then use `Array.every()` to check if every key in the first value exists in the second one and if they are equivalent by calling this method recursively.
|
||||
|
||||
```js
|
||||
const equals = (a, b) => {
|
||||
if (a === b) return true;
|
||||
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
||||
if (!a || !b || (typeof a != 'object' && typeof b !== 'object')) return a === b;
|
||||
if (a === null || a === undefined || b === null || b === undefined) return false;
|
||||
if (a.prototype !== b.prototype) return false;
|
||||
let keys = Object.keys(a);
|
||||
if (keys.length !== Object.keys(b).length) return false;
|
||||
return keys.every(k => equals(a[k], b[k]));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
equals({ a: [2, { e: 3 }], b: [4], c: 'foo' }, { a: [2, { e: 3 }], b: [4], c: 'foo' }); // true
|
||||
```
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Escapes a string for use in HTML.
|
||||
|
||||
Use `String.replace()` with a regex that matches the characters that need to be escaped, using a callback function to replace each character instance with its associated escaped character using a dictionary (object).
|
||||
Use `String.replace()` with a regexp that matches the characters that need to be escaped, using a callback function to replace each character instance with its associated escaped character using a dictionary (object).
|
||||
|
||||
```js
|
||||
const escapeHTML = str =>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Escapes a string to use in a regular expression.
|
||||
|
||||
Use `replace()` to escape special characters.
|
||||
Use `String.replace()` to escape special characters.
|
||||
|
||||
```js
|
||||
const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
|
||||
Extends a 3-digit color code to a 6-digit color code.
|
||||
|
||||
Use `Array.map()`, `split()` and `Array.join()` to join the mapped array for converting a 3-digit RGB notated hexadecimal color-code to the 6-digit form.
|
||||
`String.slice()` is used to remove `#` from string start since it's added once.
|
||||
Use `Array.map()`, `String.split()` and `Array.join()` to join the mapped array for converting a 3-digit RGB notated hexadecimal color-code to the 6-digit form.
|
||||
`Array.slice()` is used to remove `#` from string start since it's added once.
|
||||
|
||||
```js
|
||||
const extendHex = shortHex =>
|
||||
'#' +
|
||||
|
||||
20
snippets/findKey.md
Normal file
20
snippets/findKey.md
Normal file
@ -0,0 +1,20 @@
|
||||
### findKey
|
||||
|
||||
Returns the first key that satisfies the provided testing function. Otherwise `undefined` is returned.
|
||||
|
||||
Use `Object.keys(obj)` to get all the properties of the object, `Array.find()` to test the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
|
||||
|
||||
```js
|
||||
const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj));
|
||||
```
|
||||
|
||||
```js
|
||||
findKey(
|
||||
{
|
||||
barney: { age: 36, active: true },
|
||||
fred: { age: 40, active: false },
|
||||
pebbles: { age: 1, active: true }
|
||||
},
|
||||
o => o['active']
|
||||
); // 'barney'
|
||||
```
|
||||
13
snippets/findLast.md
Normal file
13
snippets/findLast.md
Normal file
@ -0,0 +1,13 @@
|
||||
### findLast
|
||||
|
||||
Returns the last element for which the provided function returns a truthy value.
|
||||
|
||||
Use `Array.filter()` to remove elements for which `fn` returns falsey values, `Array.slice(-1)` to get the last one.
|
||||
|
||||
```js
|
||||
const findLast = (arr, fn) => arr.filter(fn).slice(-1)[0];
|
||||
```
|
||||
|
||||
```js
|
||||
findLast([1, 2, 3, 4], n => n % 2 === 1); // 3
|
||||
```
|
||||
18
snippets/findLastIndex.md
Normal file
18
snippets/findLastIndex.md
Normal file
@ -0,0 +1,18 @@
|
||||
### findLastIndex
|
||||
|
||||
Returns the index of the last element for which the provided function returns a truthy value.
|
||||
|
||||
Use `Array.map()` to map each element to an array with its index and value.
|
||||
Use `Array.filter()` to remove elements for which `fn` returns falsey values, `Array.slice(-1)` to get the last one.
|
||||
|
||||
```js
|
||||
const findLastIndex = (arr, fn) =>
|
||||
arr
|
||||
.map((val, i) => [i, val])
|
||||
.filter(val => fn(val[1], val[0], arr))
|
||||
.slice(-1)[0][0];
|
||||
```
|
||||
|
||||
```js
|
||||
findLastIndex([1, 2, 3, 4], n => n % 2 === 1); // 2 (index of the value 3)
|
||||
```
|
||||
23
snippets/findLastKey.md
Normal file
23
snippets/findLastKey.md
Normal file
@ -0,0 +1,23 @@
|
||||
### findLastKey
|
||||
|
||||
Returns the last key that satisfies the provided testing function. Otherwise `undefined` is returned.
|
||||
|
||||
Use `Object.keys(obj)` to get all the properties of the object, `Array.reverse()` to reverse their order and `Array.find()` to test the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
|
||||
|
||||
```js
|
||||
const findLastKey = (obj, fn) =>
|
||||
Object.keys(obj)
|
||||
.reverse()
|
||||
.find(key => fn(obj[key], key, obj));
|
||||
```
|
||||
|
||||
```js
|
||||
findLastKey(
|
||||
{
|
||||
barney: { age: 36, active: true },
|
||||
fred: { age: 40, active: false },
|
||||
pebbles: { age: 1, active: true }
|
||||
},
|
||||
o => o['active']
|
||||
); // 'pebbles'
|
||||
```
|
||||
@ -1,13 +1,20 @@
|
||||
### flatten
|
||||
|
||||
Flattens an array.
|
||||
Flattens an array up to the specified depth.
|
||||
|
||||
Use a new array and concatenate it with the spread input array causing a shallow denesting of any contained arrays.
|
||||
Use recursion, decrementing `depth` by 1 for each level of depth.
|
||||
Use `Array.reduce()` and `Array.concat()` to merge elements or arrays.
|
||||
Base case, for `depth` equal to `1` stops recursion.
|
||||
Omit the second argument, `depth` to flatten only to a depth of `1` (single flatten).
|
||||
|
||||
```js
|
||||
const flatten = arr => [].concat(...arr);
|
||||
const flatten = (arr, depth = 1) =>
|
||||
depth != 1
|
||||
? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flatten(v, depth - 1) : v), [])
|
||||
: arr.reduce((a, v) => a.concat(v), []);
|
||||
```
|
||||
|
||||
```js
|
||||
flatten([1, [2], 3, 4]); // [1,2,3,4]
|
||||
flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
|
||||
flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
|
||||
```
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
### flattenDepth
|
||||
|
||||
Flattens an array up to the specified depth.
|
||||
|
||||
Use recursion, decrementing `depth` by 1 for each level of depth.
|
||||
Use `Array.reduce()` and `Array.concat()` to merge elements or arrays.
|
||||
Base case, for `depth` equal to `1` stops recursion.
|
||||
Omit the second element, `depth` to flatten only to a depth of `1` (single flatten).
|
||||
|
||||
```js
|
||||
const flattenDepth = (arr, depth = 1) =>
|
||||
depth != 1
|
||||
? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), [])
|
||||
: arr.reduce((a, v) => a.concat(v), []);
|
||||
```
|
||||
|
||||
```js
|
||||
flattenDepth([1, [2], 3, 4]); // [1,2,3,4]
|
||||
```
|
||||
@ -1,11 +1,11 @@
|
||||
### flip
|
||||
|
||||
Flip takes a function as an argument, then makes the first argument the last
|
||||
Flip takes a function as an argument, then makes the first argument the last.
|
||||
|
||||
Return a closure that takes variadic inputs, and splices the last argument to make it the first argument before applying the rest.
|
||||
|
||||
```js
|
||||
const flip = fn => (...args) => fn(args.pop(), ...args);
|
||||
const flip = fn => (first, ...rest) => fn(...rest, first);
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
17
snippets/forEachRight.md
Normal file
17
snippets/forEachRight.md
Normal file
@ -0,0 +1,17 @@
|
||||
### forEachRight
|
||||
|
||||
Executes a provided function once for each array element, starting from the array's last element.
|
||||
|
||||
Use `Array.slice(0)` to clone the given array, `Array.reverse()` to reverse it and `Array.forEach()` to iterate over the reversed array.
|
||||
|
||||
```js
|
||||
const forEachRight = (arr, callback) =>
|
||||
arr
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.forEach(callback);
|
||||
```
|
||||
|
||||
```js
|
||||
forEachRight([1, 2, 3, 4], val => console.log(val)); // '4', '3', '2', '1'
|
||||
```
|
||||
13
snippets/forOwn.md
Normal file
13
snippets/forOwn.md
Normal file
@ -0,0 +1,13 @@
|
||||
### forOwn
|
||||
|
||||
Iterates over all own properties of an object, running a callback for each one.
|
||||
|
||||
Use `Object.keys(obj)` to get all the properties of the object, `Array.forEach()` to run the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
|
||||
|
||||
```js
|
||||
const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj));
|
||||
```
|
||||
|
||||
```js
|
||||
forOwn({ foo: 'bar', a: 1 }, v => console.log(v)); // 'bar', 1
|
||||
```
|
||||
16
snippets/forOwnRight.md
Normal file
16
snippets/forOwnRight.md
Normal file
@ -0,0 +1,16 @@
|
||||
### forOwnRight
|
||||
|
||||
Iterates over all own properties of an object in reverse, running a callback for each one.
|
||||
|
||||
Use `Object.keys(obj)` to get all the properties of the object, `Array.reverse()` to reverse their order and `Array.forEach()` to run the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
|
||||
|
||||
```js
|
||||
const forOwnRight = (obj, fn) =>
|
||||
Object.keys(obj)
|
||||
.reverse()
|
||||
.forEach(key => fn(obj[key], key, obj));
|
||||
```
|
||||
|
||||
```js
|
||||
forOwnRight({ foo: 'bar', a: 1 }, v => console.log(v)); // 1, 'bar'
|
||||
```
|
||||
30
snippets/formatDuration.md
Normal file
30
snippets/formatDuration.md
Normal file
@ -0,0 +1,30 @@
|
||||
### formatDuration
|
||||
|
||||
Returns the human readable format of the given number of milliseconds.
|
||||
|
||||
Divide `ms` with the appropriate values to obtain the appropriate values for `day`, `hour`, `minute`, `second` and `millisecond`.
|
||||
Use `Object.entries()` with `Array.filter()` to keep only non-zero values.
|
||||
Use `Array.map()` to create the string for each value, pluralizing appropriately.
|
||||
Use `String.join(', ')` to combine the values into a string.
|
||||
|
||||
```js
|
||||
const formatDuration = ms => {
|
||||
if (ms < 0) ms = -ms;
|
||||
const time = {
|
||||
day: Math.floor(ms / 86400000),
|
||||
hour: Math.floor(ms / 3600000) % 24,
|
||||
minute: Math.floor(ms / 60000) % 60,
|
||||
second: Math.floor(ms / 1000) % 60,
|
||||
millisecond: Math.floor(ms) % 1000
|
||||
};
|
||||
return Object.entries(time)
|
||||
.filter(val => val[1] !== 0)
|
||||
.map(val => val[1] + ' ' + (val[1] !== 1 ? val[0] + 's' : val[0]))
|
||||
.join(', ');
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
formatDuration(1001); // '1 second, 1 millisecond'
|
||||
formatDuration(34325055574); // '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'
|
||||
```
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
Converts a string from camelcase.
|
||||
|
||||
Use `replace()` to remove underscores, hyphens, and spaces and convert words to camelcase.
|
||||
Omit the second argument to use a default separator of `_`.
|
||||
Use `String.replace()` to remove underscores, hyphens, and spaces and convert words to camelcase.
|
||||
Omit the second argument to use a default `separator` of `_`.
|
||||
|
||||
```js
|
||||
const fromCamelCase = (str, separator = '_') =>
|
||||
|
||||
26
snippets/functions.md
Normal file
26
snippets/functions.md
Normal file
@ -0,0 +1,26 @@
|
||||
### functions
|
||||
|
||||
Returns an array of function property names from own (and optionally inherited) enumerable properties of an object.
|
||||
|
||||
Use `Object.keys(obj)` to iterate over the object's own properties.
|
||||
If `inherited` is `true`, use `Object.get.PrototypeOf(obj)` to also get the object's inherited properties.
|
||||
Use `Array.filter()` to keep only those properties that are functions.
|
||||
Omit the second argument, `inherited`, to not include inherited properties by default.
|
||||
|
||||
```js
|
||||
const functions = (obj, inherited = false) =>
|
||||
(inherited
|
||||
? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))]
|
||||
: Object.keys(obj)
|
||||
).filter(key => typeof obj[key] === 'function');
|
||||
```
|
||||
|
||||
```js
|
||||
function Foo() {
|
||||
this.a = () => 1;
|
||||
this.b = () => 2;
|
||||
}
|
||||
Foo.prototype.c = () => 3;
|
||||
functions(new Foo()); // ['a', 'b']
|
||||
functions(new Foo(), true); // ['a', 'b', 'c']
|
||||
```
|
||||
@ -2,18 +2,18 @@
|
||||
|
||||
Calculates the greatest common divisor between two or more numbers/arrays.
|
||||
|
||||
The `helperGcd `function uses recursion.
|
||||
The inner `_gcd` function uses recursion.
|
||||
Base case is when `y` equals `0`. In this case, return `x`.
|
||||
Otherwise, return the GCD of `y` and the remainder of the division `x/y`.
|
||||
|
||||
```js
|
||||
const gcd = (...arr) => {
|
||||
let data = [].concat(...arr);
|
||||
const helperGcd = (x, y) => (!y ? x : gcd(y, x % y));
|
||||
return data.reduce((a, b) => helperGcd(a, b));
|
||||
const _gcd = (x, y) => (!y ? x : gcd(y, x % y));
|
||||
return [...arr].reduce((a, b) => _gcd(a, b));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
gcd(8, 36); // 4
|
||||
gcd(...[12, 8, 32]); // 4
|
||||
```
|
||||
|
||||
21
snippets/geometricProgression.md
Normal file
21
snippets/geometricProgression.md
Normal file
@ -0,0 +1,21 @@
|
||||
### geometricProgression
|
||||
|
||||
Initializes an array containing the numbers in the specified range where `start` and `end` are inclusive and the ratio between two terms is `step`.
|
||||
Returns an error if `step` equals `1`.
|
||||
|
||||
Use `Array.from()`, `Math.log()` and `Math.floor()` to create an array of the desired length, `Array.map()` to fill with the desired values in a range.
|
||||
Omit the second argument, `start`, to use a default value of `1`.
|
||||
Omit the third argument, `step`, to use a default value of `2`.
|
||||
|
||||
```js
|
||||
const geometricProgression = (end, start = 1, step = 2) =>
|
||||
Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map(
|
||||
(v, i) => start * step ** i
|
||||
);
|
||||
```
|
||||
|
||||
```js
|
||||
geometricProgression(256); // [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
||||
geometricProgression(256, 3); // [3, 6, 12, 24, 48, 96, 192]
|
||||
geometricProgression(256, 1, 4); // [1, 4, 16, 64, 256]
|
||||
```
|
||||
21
snippets/get.md
Normal file
21
snippets/get.md
Normal file
@ -0,0 +1,21 @@
|
||||
### get
|
||||
|
||||
Retrieve a set of properties indicated by the given selectors from an object.
|
||||
|
||||
Use `Array.map()` for each selector, `String.replace()` to replace square brackets with dots, `String.split('.')` to split each selector, `Array.filter()` to remove empty values and `Array.reduce()` to get the value indicated by it.
|
||||
|
||||
```js
|
||||
const get = (from, ...selectors) =>
|
||||
[...selectors].map(s =>
|
||||
s
|
||||
.replace(/\[([^\[\]]*)\]/g, '.$1.')
|
||||
.split('.')
|
||||
.filter(t => t !== '')
|
||||
.reduce((prev, cur) => prev && prev[cur], from)
|
||||
);
|
||||
```
|
||||
|
||||
```js
|
||||
const obj = { selector: { to: { val: 'val to select' } }, target: [1, 2, { a: 'test' }] };
|
||||
get(obj, 'selector.to.val', 'target[0]', 'target[2].a'); // ['val to select', 1, 'test']
|
||||
```
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Returns the native type of a value.
|
||||
|
||||
Returns lowercased constructor name of value, "undefined" or "null" if value is undefined or null
|
||||
Returns lowercased constructor name of value, `"undefined"` or `"null"` if value is `undefined` or `null`.
|
||||
|
||||
```js
|
||||
const getType = v =>
|
||||
@ -10,5 +10,5 @@ const getType = v =>
|
||||
```
|
||||
|
||||
```js
|
||||
getType(new Set([1, 2, 3])); // "set"
|
||||
getType(new Set([1, 2, 3])); // 'set'
|
||||
```
|
||||
|
||||
@ -2,16 +2,18 @@
|
||||
|
||||
Returns an object containing the parameters of the current URL.
|
||||
|
||||
Use `match()` with an appropriate regular expression to get all key-value pairs, `Array.reduce()` to map and combine them into a single object.
|
||||
Use `String.match()` with an appropriate regular expression to get all key-value pairs, `Array.reduce()` to map and combine them into a single object.
|
||||
Pass `location.search` as the argument to apply to the current `url`.
|
||||
|
||||
```js
|
||||
const getURLParameters = url =>
|
||||
url
|
||||
.match(/([^?=&]+)(=([^&]*))/g)
|
||||
.reduce((a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {});
|
||||
(url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
|
||||
(a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a),
|
||||
{}
|
||||
);
|
||||
```
|
||||
|
||||
```js
|
||||
getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'}
|
||||
getURLParameters('google.com'); // {}
|
||||
```
|
||||
|
||||
@ -6,8 +6,8 @@ Use `Array.map()` to map the values of an array to a function or property name.
|
||||
Use `Array.reduce()` to create an object, where the keys are produced from the mapped results.
|
||||
|
||||
```js
|
||||
const groupBy = (arr, func) =>
|
||||
arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
|
||||
const groupBy = (arr, fn) =>
|
||||
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
|
||||
acc[val] = (acc[val] || []).concat(arr[i]);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
18
snippets/hasFlags.md
Normal file
18
snippets/hasFlags.md
Normal file
@ -0,0 +1,18 @@
|
||||
### hasFlags
|
||||
|
||||
Check if the current process's arguments contain the specified flags.
|
||||
|
||||
Use `Array.every()` and `Array.includes()` to check if `process.argv` contains all the specified flags.
|
||||
Use a regular expression to test if the specified flags are prefixed with `-` or `--` and prefix them accordingly.
|
||||
|
||||
```js
|
||||
const hasFlags = (...flags) =>
|
||||
flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag));
|
||||
```
|
||||
|
||||
```js
|
||||
// node myScript.js -s --test --cool=true
|
||||
hasFlags('-s'); // true
|
||||
hasFlags('--test', 'cool=true', '-s'); // true
|
||||
hasFlags('special'); // false
|
||||
```
|
||||
20
snippets/hashBrowser.md
Normal file
20
snippets/hashBrowser.md
Normal file
@ -0,0 +1,20 @@
|
||||
### hashBrowser
|
||||
|
||||
Creates a hash for a value using the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) algorithm. Returns a promise.
|
||||
|
||||
Use the [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) API to create a hash for the given value.
|
||||
|
||||
```js
|
||||
const hashBrowser = val =>
|
||||
crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => {
|
||||
let hexes = [],
|
||||
view = new DataView(h);
|
||||
for (let i = 0; i < view.byteLength; i += 4)
|
||||
hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
|
||||
return hexes.join('');
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
|
||||
```
|
||||
26
snippets/hashNode.md
Normal file
26
snippets/hashNode.md
Normal file
@ -0,0 +1,26 @@
|
||||
### hashNode
|
||||
|
||||
Creates a hash for a value using the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) algorithm. Returns a promise.
|
||||
|
||||
Use `crypto` API to create a hash for the given value.
|
||||
|
||||
```js
|
||||
const crypto = require('crypto');
|
||||
const hashNode = val =>
|
||||
new Promise(resolve =>
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve(
|
||||
crypto
|
||||
.createHash('sha256')
|
||||
.update(val)
|
||||
.digest('hex')
|
||||
),
|
||||
0
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
```js
|
||||
hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
|
||||
```
|
||||
@ -9,5 +9,5 @@ const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
|
||||
```
|
||||
|
||||
```js
|
||||
hide(document.querySelectorAll('img')); // Hides all <img> elements on the page
|
||||
hide(...document.querySelectorAll('img')); // Hides all <img> elements on the page
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user