Merge pull request #1 from Chalarangelo/master

merged upstream/master
This commit is contained in:
Rob-Rychs
2018-01-27 11:36:09 -06:00
committed by GitHub
841 changed files with 24903 additions and 2505 deletions

View File

@ -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 -->

View File

@ -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
View File

@ -3,5 +3,4 @@ currentSnippet\.js
*.md.temp.js
.idea
test.sh
dist/
test/
/*.log

View File

@ -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

View File

@ -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
}

View File

@ -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.**

View File

@ -1,23 +1,23 @@
## Contributing
![contribution guidelines](https://i.imgur.com/8Wk9nat.png)
**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.

4627
README.md

File diff suppressed because it is too large Load Diff

1
advanced.svg Normal file
View 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
View 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

File diff suppressed because one or more lines are too long

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

1
docs/clipboard.svg Normal file
View 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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
View 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;
}
}

View 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
View 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;
}
}
}
}

View File

@ -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});
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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": {}
}

View File

@ -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 = `![Logo](/logo.png)
# 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);
// ![advanced](/advanced.svg)
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] +' ![advanced](/advanced.svg)';
data = data.join('\n');
}
data =
data.slice(0, data.lastIndexOf('```js')) +
'<details>\n<summary>Examples</summary>\n\n' +

View File

@ -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
View 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
View 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'
});
})();

View File

@ -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`

View File

@ -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');

View File

@ -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(
/&amp;|&lt;|&gt;|&#39;|&quot;/g,
tag =>
({
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&#39;': "'",
'&quot;': '"'
}[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">&nbsp;</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">&#128203;&nbsp;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">&#128203;&nbsp;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');

View File

@ -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
View 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'
```

View File

@ -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
View 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
View 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'
```

View File

@ -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
View 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
View 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
View 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
View 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
View 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'
```

View File

@ -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`.

View File

@ -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

View File

@ -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
View 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]
```

View File

@ -20,10 +20,6 @@ chainAsync([
},
next => {
console.log('1 second');
setTimeout(next, 1000);
},
next => {
console.log('2 seconds');
}
]);
```

View File

@ -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
View 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
View 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
View 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
```

View 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
View 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}
```

View File

@ -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
View 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'
```

View File

@ -0,0 +1,45 @@
### createEventHub
Creates a pub/sub ([publishsubscribe](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
View 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
View 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
View 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
View 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
View 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.
```

View File

@ -12,6 +12,5 @@ const detectDeviceType = () =>
```
```js
detectDeviceType(); // "Mobile"
detectDeviceType(); // "Desktop"
detectDeviceType(); // "Mobile" or "Desktop"
```

17
snippets/differenceBy.md Normal file
View 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 } ]
```

View File

@ -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]
```

View File

@ -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

View File

@ -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
View 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); // []
```

View 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]
```

View File

@ -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]
```

View File

@ -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
View 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
View 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
```

View File

@ -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 =>

View File

@ -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, '\\$&');

View File

@ -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
View 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
View 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
View 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
View 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'
```

View File

@ -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]
```

View File

@ -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]
```

View File

@ -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
View 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
View 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
View 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'
```

View 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'
```

View File

@ -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
View 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']
```

View File

@ -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
```

View 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
View 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']
```

View File

@ -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'
```

View File

@ -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'); // {}
```

View File

@ -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
View 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
View 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
View 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'
```

View File

@ -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