diff --git a/README.md b/README.md index 9cdeb42e2..d302b8268 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ * [`initializeArrayWithRange`](#initializearraywithrange) * [`initializeArrayWithValues`](#initializearraywithvalues) * [`intersection`](#intersection) +* [`join`](#join) * [`last`](#last) * [`mapObject`](#mapobject) * [`nthElement`](#nthelement) @@ -64,8 +65,10 @@ * [`quickSort`](#quicksort) * [`remove`](#remove) * [`sample`](#sample) +* [`sampleSize`](#samplesize) * [`shuffle`](#shuffle) * [`similarity`](#similarity) +* [`sortedIndex`](#sortedindex) * [`symmetricDifference`](#symmetricdifference) * [`tail`](#tail) * [`take`](#take) @@ -77,7 +80,7 @@ -### 🖥️ Browser +### 🌐 Browser
View contents @@ -90,7 +93,6 @@ * [`elementIsVisibleInViewport`](#elementisvisibleinviewport) * [`getScrollPosition`](#getscrollposition) * [`getStyle`](#getstyle) -* [`getURLParameters`](#geturlparameters) * [`hasClass`](#hasclass) * [`hide`](#hide) * [`httpsRedirect`](#httpsredirect) @@ -126,6 +128,7 @@ * [`compose`](#compose) * [`curry`](#curry) * [`functionName`](#functionname) +* [`memoize`](#memoize) * [`runPromisesInSeries`](#runpromisesinseries) * [`sleep`](#sleep) @@ -150,6 +153,7 @@ * [`collatz`](#collatz) * [`digitize`](#digitize) * [`distance`](#distance) +* [`elo`](#elo) * [`factorial`](#factorial) * [`fibonacci`](#fibonacci) * [`fibonacciCountUntilNum`](#fibonaccicountuntilnum) @@ -217,6 +221,8 @@ * [`escapeHTML`](#escapehtml) * [`escapeRegExp`](#escaperegexp) * [`fromCamelCase`](#fromcamelcase) +* [`isAbsoluteURL`](#isabsoluteurl) +* [`mask`](#mask) * [`palindrome`](#palindrome) * [`repeatString`](#repeatstring) * [`reverseString`](#reversestring) @@ -240,14 +246,19 @@ * [`coalesceFactory`](#coalescefactory) * [`extendHex`](#extendhex) * [`getType`](#gettype) +* [`getURLParameters`](#geturlparameters) * [`hexToRGB`](#hextorgb) * [`isArray`](#isarray) +* [`isArrayLike`](#isarraylike) * [`isBoolean`](#isboolean) * [`isFunction`](#isfunction) * [`isNull`](#isnull) * [`isNumber`](#isnumber) +* [`isPrimitive`](#isprimitive) +* [`isPromiseLike`](#ispromiselike) * [`isString`](#isstring) * [`isSymbol`](#issymbol) +* [`isValidJSON`](#isvalidjson) * [`randomHexColorCode`](#randomhexcolorcode) * [`RGBToHex`](#rgbtohex) * [`sdbm`](#sdbm) @@ -903,6 +914,40 @@ intersection([1, 2, 3], [4, 3, 2]); // [2,3]
[⬆ Back to top](#table-of-contents) +### join + +Joins all elements of an array into a string and returns this string. Uses a separator and an end separator. + +Use `Array.reduce()` to combine elements into a string. +Omit the second argument, `separator`, to use a default separator of `','`. +Omit the third argument, `end`, to use the same value as `separator` by default. + +```js +const join = (arr, separator = ',', end = separator) => + arr.reduce( + (acc, val, i) => + i == arr.length - 2 + ? acc + val + end + : i == arr.length - 1 ? acc + val : acc + val + separator, + '' + ); +``` + +
+Examples + +```js +join(); // '' +join(['pen', 'pineapple', 'apple', 'pen'], ',', '&'); //"pen,pineapple,apple&pen" +join(['pen', 'pineapple', 'apple', 'pen'], ','); //"pen,pineapple,apple,pen" +join(['pen', 'pineapple', 'apple', 'pen']); //"pen,pineapple,apple,pen" +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### last Returns the last element in an array. @@ -1189,6 +1234,38 @@ sample([3, 7, 9, 11]); // 9
[⬆ Back to top](#table-of-contents) +### sampleSize + +Gets `n` random elements at unique keys from `array` up to the size of `array`. + +Shuffle the array using the [Fisher-Yates algorithm](https://github.com/chalarangelo/30-seconds-of-code#shuffle). +Use `Array.slice()` to get the first `n` elements. +Omit the second argument, `n` to get only one element at random from the array. + +```js +const sampleSize = ([...arr], n = 1) => { + let m = arr.length; + while (m) { + const i = Math.floor(Math.random() * m--); + [arr[m], arr[i]] = [arr[i], arr[m]]; + } + return arr.slice(0, n); +}; +``` + +
+Examples + +```js +sampleSize([1, 2, 3], 2); // [3,1] +sampleSize([1, 2, 3], 4); // [2,3,1] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### shuffle Randomizes the order of the values of an array, returning a new array. @@ -1242,6 +1319,34 @@ similarity([1, 2, 3], [1, 2, 4]); // [1,2]
[⬆ Back to top](#table-of-contents) +### sortedIndex + +Returns the lowest index at which value should be inserted into array in order to maintain its sort order. + +Check if the array is sorted in descending order (loosely). +Use `Array.findIndex()` to find the appropriate index where the element should be inserted. + +```js +const sortedIndex = (arr, n) => { + const isDescending = arr[0] > arr[arr.length - 1]; + const index = arr.findIndex(el => (isDescending ? n >= el : n <= el)); + return index === -1 ? arr.length : index; +}; +``` + +
+Examples + +```js +sortedIndex([5, 3, 2, 1], 4); // 1 +sortedIndex([30, 50], 40); // 1 +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### symmetricDifference Returns the symmetric difference between two arrays. @@ -1437,7 +1542,7 @@ zipObject(['a', 'b'], [1, 2, 3]); // {a: 1, b: 2}
[⬆ Back to top](#table-of-contents) --- - ## 🖥️ Browser + ## 🌐 Browser ### arrayToHtmlList @@ -1658,32 +1763,6 @@ getStyle(document.querySelector('p'), 'font-size'); // '16px'
[⬆ Back to top](#table-of-contents) -### getURLParameters - -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. -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), {}); -``` - -
-Examples - -```js -getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'} -``` - -
- -
[⬆ Back to top](#table-of-contents) - - ### hasClass Returns `true` if the element has the specified class, `false` otherwise. @@ -2176,6 +2255,35 @@ functionName(Math.max); // max (logged in debug channel of console)
[⬆ Back to top](#table-of-contents) +### memoize + +Returns the memoized (cached) function. + +Use `Object.create(null)` to create an empty object without `Object.prototype` (so that those properties are not resolved if the input value is something like `'hasOwnProperty'`). +Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not. + +```js +const memoize = fn => { + const cache = Object.create(null); + return value => cache[value] || (cache[value] = fn(value)); +}; +``` + +
+Examples + +```js +// See the `anagrams` snippet. +const anagramsCached = memoize(anagrams); +anagramsCached('javascript'); // takes a long time +anagramsCached('javascript'); // returns virtually instantly since it's now cached +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### runPromisesInSeries Runs an array of promises in series. @@ -2371,6 +2479,39 @@ distance(1, 1, 2, 3); // 2.23606797749979
[⬆ Back to top](#table-of-contents) +### elo + +Computes the new ratings between two opponents using the [Elo rating system](https://en.wikipedia.org/wiki/Elo_rating_system). It takes an array +of two pre-ratings and returns an array containing two post-ratings. +The winner's rating is the first element of the array. + +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. Omit the second argument to use the default +K-factor of 32, or supply a custom K-factor value. + +```js +const elo = ([a, b], kFactor = 32) => { + const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400)); + const newRating = (rating, i) => rating + kFactor * (i - expectedScore(i ? a : b, i ? b : a)); + return [newRating(a, 1), newRating(b, 0)]; +}; +``` + +
+Examples + +```js +elo([1200, 1200]); // [1216, 1184] +elo([1000, 2000]); // [1031.8991261061358, 1968.1008738938642] +elo([1500, 1000]); // [1501.7036868864648, 998.2963131135352] +elo([1200, 1200], 64); // [1232, 1168] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### factorial Calculates the factorial of a number. @@ -2565,11 +2706,11 @@ inrange(3, 2); // false Checks if the given number is an Armstrong number or not. -Convert the given number into an array of digits. Use `Math.pow()` to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return `true` otherwise `false`. +Convert the given number into an array of digits. Use the exponent operator (`**`) to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return `true` otherwise `false`. ```js const isArmstrongNumber = digits => - (arr => arr.reduce((a, d) => a + Math.pow(parseInt(d), arr.length), 0) == digits)( + (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)( (digits + '').split('') ); ``` @@ -2913,9 +3054,7 @@ You can omit the second argument to get the sample standard deviation or set it const standardDeviation = (arr, usePopulation = false) => { const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length; return Math.sqrt( - arr - .reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), []) - .reduce((acc, val) => acc + val, 0) / + arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) / (arr.length - (usePopulation ? 0 : 1)) ); }; @@ -3355,7 +3494,7 @@ byteSize('Hello World'); // 11
[⬆ Back to top](#table-of-contents) -### Capitalize +### capitalize Capitalizes the first letter of a string. @@ -3509,6 +3648,59 @@ fromCamelCase('someJavascriptProperty', '_'); // 'some_javascript_property'
[⬆ Back to top](#table-of-contents) +### isAbsoluteURL + +Returns `true` if the given string is an absolute URL, `false` otherwise. + +Use a regular expression to test if the string is an absolute URL. + +```js +const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str); +``` + +
+Examples + +```js +isAbsoluteURL('https://google.com'); // true +isAbsoluteURL('ftp://www.myserver.net'); // true +isAbsoluteURL('/foo/bar'); // false +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + +### mask + +Replaces all but the last `num` of characters with the specified mask character. + +Use `String.slice()` to grab the portion of the characters that need to be masked and use `String.replace()` with a regex to replace every character with the mask character. +Concatenate the masked characters with the remaining unmasked portion of the string. +Omit the second argument, `num`, to keep a default of `4` characters unmasked. If `num` is negative, the unmasked characters will be at the start of the string. +Omit the third argument, `mask`, to use a default character of `'*'` for the mask. + +```js +const mask = (cc, num = 4, mask = '*') => + ('' + cc).slice(0, -num).replace(/./g, mask) + ('' + cc).slice(-num); +``` + +
+Examples + +```js +mask(1234567890); // '******7890' +mask(1234567890, 3); // '*******890' +mask(1234567890, 4, '$'); // '$$$$$$7890' +mask(1234567890, -4, '$'); // '1234$$$$$$' +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### palindrome Returns `true` if the given string is a palindrome, `false` otherwise. @@ -3919,6 +4111,32 @@ getType(new Set([1, 2, 3])); // "set"
[⬆ Back to top](#table-of-contents) +### getURLParameters + +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. +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), {}); +``` + +
+Examples + +```js +getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'} +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### hexToRGB Converts a color code to a `rgb()` or `rgba()` string if alpha value is provided. @@ -3984,6 +4202,35 @@ isArray([1]); // true
[⬆ Back to top](#table-of-contents) +### isArrayLike + +Checks if the provided argument is array-like (i.e. is iterable). + +Use the spread operator (`...`) to check if the provided argument is iterable inside a `try... catch` block and the comma operator (`,`) to return the appropriate value. + +```js + + +const isArrayLike = val => + try {return [...val], true; } + catch (e) { return false; } +}; +``` + +
+Examples + +```js +isArrayLike(document.querySelectorAll('.className')); // true +isArrayLike('abc'); // true +isArrayLike(null); // false +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### isBoolean Checks if the given argument is a native boolean element. @@ -4075,6 +4322,68 @@ isNumber(1); // true
[⬆ Back to top](#table-of-contents) +### isPrimitive + +Returns a boolean determining if the supplied value is primitive or not. + +Use `Array.includes()` on an array of type strings which are not primitive, +supplying the type using `typeof`. +Since `typeof null` evaluates to `'object'`, it needs to be directly compared. + +```js +const isPrimitive = val => !['object', 'function'].includes(typeof val) || val === null; +``` + +
+Examples + +```js +isPrimitive(window.someNonExistentProperty); // true +isPrimitive(null); // true +isPrimitive(50); // true +isPrimitive('Hello!'); // true +isPrimitive(false); // true +isPrimitive(Symbol()); // true +isPrimitive([]); // false +isPrimitive(new String('Hello!')); // false +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + +### isPromiseLike + +Returns `true` if an object looks like a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), `false` otherwise. + +Check if the object is not `null`, its `typeof` matches either `object` or `function` and if it has a `.then` property, which is also a `function`. + +```js +const isPromiseLike = obj => + obj !== null && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function'; +``` + +
+Examples + +```js +isPromiseLike({ + then: function() { + return ''; + } +}); // true +isPromiseLike(null); // false +isPromiseLike({}); // false +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### isString Checks if the given argument is a string. @@ -4121,6 +4430,37 @@ isSymbol(Symbol('x')); // true
[⬆ Back to top](#table-of-contents) +### isValidJSON + +Checks if the provided argument is a valid JSON. + +Use `JSON.parse()` and a `try... catch` block to check if the provided argument is a valid JSON. + +```js +const isValidJSON = obj => { + try { + JSON.parse(obj); + return true; + } catch (e) { + return false; + } +}; +``` + +
+Examples + +```js +isValidJSON('{"name":"Adam","age":20}'); // true +isValidJSON('{"name":"Adam",age:"20"}'); // false +isValidJSON(null); // true +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + ### randomHexColorCode Generates a random hexadecimal color code. @@ -4332,6 +4672,13 @@ yesNo('Foo', true); // true
[⬆ Back to top](#table-of-contents) +## Collaborators + +| [](https://github.com/Chalarangelo)
[Angelos Chalaris](https://github.com/Chalarangelo) | [](https://github.com/Pl4gue)
[David Wu](https://github.com/Pl4gue) | [](https://github.com/fejes713)
[Stefan Feješ](https://github.com/fejes713) | [](https://github.com/kingdavidmartins)
[King David Martins](https://github.com/iamsoorena) | [](https://github.com/iamsoorena)
[Soorena Soleimani](https://github.com/iamsoorena) | +| --- | --- | --- | --- | --- | +| [](https://github.com/elderhsouza)
[Elder Henrique Souza](https://github.com/elderhsouza) | [](https://github.com/skatcat31)
[Robert Mennell](https://github.com/skatcat31) | [](https://github.com/atomiks)
[atomiks](https://github.com/atomiks) | + + ## Credits *Icons made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/).* diff --git a/docs/index.html b/docs/index.html index 2acf4f2bb..4f5e25ed6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -59,7 +59,7 @@ wrapper.appendChild(box); box.appendChild(el); }); - }

 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.

 

Adapter

call

Given a key and a set of arguments, call them when given a context. Primarily useful in composition.

Use a closure to call a stored key with stored arguments.

const call = (key, ...args) => context => context[key](...args);
+    }

 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.

 

Adapter

call

Given a key and a set of arguments, call them when given a context. Primarily useful in composition.

Use a closure to call a stored key with stored arguments.

const call = (key, ...args) => context => context[key](...args);
 
Promise.resolve([1, 2, 3])
   .then(call('map', x => 2 * x))
   .then(console.log); //[ 2, 4, 6 ]
@@ -163,6 +163,18 @@ initializeArrayWithRange(7, 3); // [3,4,5,6,7]
   return a.filter(x => s.has(x));
 };
 
intersection([1, 2, 3], [4, 3, 2]); // [2,3]
+

join

Joins all elements of an array into a string and returns this string. Uses a separator and an end separator.

Use Array.reduce() to combine elements into a string. Omit the second argument, separator, to use a default separator of ','. Omit the third argument, end, to use the same value as separator by default.

const join = (arr, separator = ',', end = separator) =>
+  arr.reduce(
+    (acc, val, i) =>
+      i == arr.length - 2
+        ? acc + val + end
+        : i == arr.length - 1 ? acc + val : acc + val + separator,
+    ''
+  );
+
join(); // ''
+join(['pen', 'pineapple', 'apple', 'pen'], ',', '&'); //"pen,pineapple,apple&pen"
+join(['pen', 'pineapple', 'apple', 'pen'], ','); //"pen,pineapple,apple,pen"
+join(['pen', 'pineapple', 'apple', 'pen']); //"pen,pineapple,apple,pen"
 

last

Returns the last element in an array.

Use arr.length - 1 to compute the index of the last element of the given array and returning it.

const last = arr => arr[arr.length - 1];
 
last([1, 2, 3]); // 3
 

mapObject

Maps the values of an array to an object using a function, where the key-value pairs consist of the original value as the key and the mapped value.

Use an anonymous inner function scope to declare an undefined memory space, using closures to store a return value. Use a new Array to store the array with a map of the function over its data set and a comma operator to return a second step, without needing to move from one context to another (due to closures and order of operations).

const mapObject = (arr, fn) =>
@@ -236,6 +248,16 @@ quickSort([4, 1, 3, 2], true); // [4,3,2,1]
 
remove([1, 2, 3, 4], n => n % 2 == 0); // [2, 4]
 

sample

Returns a random element from an array.

Use Math.random() to generate a random number, multiply it by length and round it of to the nearest whole number using Math.floor(). This method also works with strings.

const sample = arr => arr[Math.floor(Math.random() * arr.length)];
 
sample([3, 7, 9, 11]); // 9
+

sampleSize

Gets n random elements at unique keys from array up to the size of array.

Shuffle the array using the Fisher-Yates algorithm. Use Array.slice() to get the first n elements. Omit the second argument, n to get only one element at random from the array.

const sampleSize = ([...arr], n = 1) => {
+  let m = arr.length;
+  while (m) {
+    const i = Math.floor(Math.random() * m--);
+    [arr[m], arr[i]] = [arr[i], arr[m]];
+  }
+  return arr.slice(0, n);
+};
+
sampleSize([1, 2, 3], 2); // [3,1]
+sampleSize([1, 2, 3], 4); // [2,3,1]
 

shuffle

Randomizes the order of the values of an array, returning a new array.

Uses the Fisher-Yates algoritm to reorder the elements of the array, based on the Lodash implementation, but as a pure function.

const shuffle = ([...arr]) => {
   let m = arr.length;
   while (m) {
@@ -249,6 +271,13 @@ shuffle(foo); // [2,3,1]
 console.log(foo); // [1,2,3]
 

similarity

Returns an array of elements that appear in both arrays.

Use filter() to remove values that are not part of values, determined using includes().

const similarity = (arr, values) => arr.filter(v => values.includes(v));
 
similarity([1, 2, 3], [1, 2, 4]); // [1,2]
+

sortedIndex

Returns the lowest index at which value should be inserted into array in order to maintain its sort order.

Check if the array is sorted in descending order (loosely). Use Array.findIndex() to find the appropriate index where the element should be inserted.

const sortedIndex = (arr, n) => {
+  const isDescending = arr[0] > arr[arr.length - 1];
+  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
+  return index === -1 ? arr.length : index;
+};
+
sortedIndex([5, 3, 2, 1], 4); // 1
+sortedIndex([30, 50], 40); // 1
 

symmetricDifference

Returns the symmetric difference between two arrays.

Create a Set from each array, then use Array.filter() on each of them to only keep values not contained in the other.

const symmetricDifference = (a, b) => {
   const sA = new Set(a),
     sB = new Set(b);
@@ -331,11 +360,6 @@ elementIsVisibleInViewport(el, true); // true // (partially visible)
 
getScrollPosition(); // {x: 0, y: 200}
 

getStyle

Returns the value of a CSS rule for the specified element.

Use Window.getComputedStyle() to get the value of the CSS rule for the specified element.

const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
 
getStyle(document.querySelector('p'), 'font-size'); // '16px'
-

getURLParameters

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. Pass location.search as the argument to apply to the current url.

const getURLParameters = url =>
-  url
-    .match(/([^?=&]+)(=([^&]*))/g)
-    .reduce((a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {});
-
getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'}
 

hasClass

Returns true if the element has the specified class, false otherwise.

Use element.classList.contains() to check if the element has the specified class.

const hasClass = (el, className) => el.classList.contains(className);
 
hasClass(document.querySelector('p.special'), 'special'); // true
 

hide

Hides all the elements specified.

Use the spread operator (...) and Array.forEach() to apply display: none to each element specified.

const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
@@ -436,6 +460,14 @@ multiplyAndAdd5(5, 2); // 15
 curry(Math.min, 3)(10)(50)(2); // 2
 

functionName

Logs the name of a function.

Use console.debug() and the name property of the passed method to log the method's name to the debug channel of the console.

const functionName = fn => (console.debug(fn.name), fn);
 
functionName(Math.max); // max (logged in debug channel of console)
+

memoize

Returns the memoized (cached) function.

Use Object.create(null) to create an empty object without Object.prototype (so that those properties are not resolved if the input value is something like 'hasOwnProperty'). Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not.

const memoize = fn => {
+  const cache = Object.create(null);
+  return value => cache[value] || (cache[value] = fn(value));
+};
+
// See the `anagrams` snippet.
+const anagramsCached = memoize(anagrams);
+anagramsCached('javascript'); // takes a long time
+anagramsCached('javascript'); // returns virtually instantly since it's now cached
 

runPromisesInSeries

Runs an array of promises in series.

Use Array.reduce() to create a promise chain, where each promise returns the next promise when resolved.

const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
 
const delay = d => new Promise(r => setTimeout(r, d));
 runPromisesInSeries([() => delay(1000), () => delay(2000)]); // //executes each promise sequentially, taking a total of 3 seconds to complete
@@ -465,6 +497,15 @@ collatz(5); // 16
 
digitize(123); // [1, 2, 3]
 

distance

Returns the distance between two points.

Use Math.hypot() to calculate the Euclidean distance between two points.

const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
 
distance(1, 1, 2, 3); // 2.23606797749979
+

elo

Computes the new ratings between two opponents using the Elo rating system. It takes an array of two pre-ratings and returns an array containing two post-ratings. The winner's rating is the first element of the array.

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. Omit the second argument to use the default K-factor of 32, or supply a custom K-factor value.

const elo = ([a, b], kFactor = 32) => {
+  const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
+  const newRating = (rating, i) => rating + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
+  return [newRating(a, 1), newRating(b, 0)];
+};
+
elo([1200, 1200]); // [1216, 1184]
+elo([1000, 2000]); // [1031.8991261061358, 1968.1008738938642]
+elo([1500, 1000]); // [1501.7036868864648, 998.2963131135352]
+elo([1200, 1200], 64); // [1232, 1168]
 

factorial

Calculates the factorial of a number.

Use recursion. If n is less than or equal to 1, return 1. Otherwise, return the product of n and the factorial of n - 1. Throws an exception if n is a negative number.

const factorial = n =>
   n < 0
     ? (() => {
@@ -505,8 +546,8 @@ collatz(5); // 16
 inRange(3, 4); // true
 inRange(2, 3, 5); // false
 inrange(3, 2); // false
-

isArmstrongNumber

Checks if the given number is an Armstrong number or not.

Convert the given number into an array of digits. Use Math.pow() to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return true otherwise false.

const isArmstrongNumber = digits =>
-  (arr => arr.reduce((a, d) => a + Math.pow(parseInt(d), arr.length), 0) == digits)(
+

isArmstrongNumber

Checks if the given number is an Armstrong number or not.

Convert the given number into an array of digits. Use the exponent operator (**) to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return true otherwise false.

const isArmstrongNumber = digits =>
+  (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)(
     (digits + '').split('')
   );
 
isArmstrongNumber(1634); // true
@@ -563,9 +604,7 @@ median([0, 10, -2, 7]); // 3.5
 

standardDeviation

Returns the standard deviation of an array of numbers.

Use Array.reduce() to calculate the mean, variance and the sum of the variance of the values, the variance of the values, then determine the standard deviation. You can omit the second argument to get the sample standard deviation or set it to true to get the population standard deviation.

const standardDeviation = (arr, usePopulation = false) => {
   const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
   return Math.sqrt(
-    arr
-      .reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), [])
-      .reduce((acc, val) => acc + val, 0) /
+    arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) /
       (arr.length - (usePopulation ? 0 : 1))
   );
 };
@@ -672,7 +711,7 @@ size({ one: 1, two: 2, three: 3 }); // 3
 

byteSize

Returns the length of string.

Convert a given string to a Blob Object and find its size.

const byteSize = str => new Blob([str]).size;
 
byteSize('😀'); // 4
 byteSize('Hello World'); // 11
-

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. Omit the lowerRest parameter to keep the rest of the string intact, or set it to true to convert to lowercase.

const capitalize = ([first, ...rest], lowerRest = false) =>
+

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. Omit the lowerRest parameter to keep the rest of the string intact, or set it to true to convert to lowercase.

const capitalize = ([first, ...rest], lowerRest = false) =>
   first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
 
capitalize('fooBar'); // 'FooBar'
 capitalize('fooBar', true); // 'Foobar'
@@ -704,6 +743,16 @@ countVowels('gym'); // 0
 
fromCamelCase('someDatabaseFieldName', ' '); // 'some database field name'
 fromCamelCase('someLabelThatNeedsToBeCamelized', '-'); // 'some-label-that-needs-to-be-camelized'
 fromCamelCase('someJavascriptProperty', '_'); // 'some_javascript_property'
+

isAbsoluteURL

Returns true if the given string is an absolute URL, false otherwise.

Use a regular expression to test if the string is an absolute URL.

const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
+
isAbsoluteURL('https://google.com'); // true
+isAbsoluteURL('ftp://www.myserver.net'); // true
+isAbsoluteURL('/foo/bar'); // false
+

mask

Replaces all but the last num of characters with the specified mask character.

Use String.slice() to grab the portion of the characters that need to be masked and use String.replace() with a regex to replace every character with the mask character. Concatenate the masked characters with the remaining unmasked portion of the string. Omit the second argument, num, to keep a default of 4 characters unmasked. If num is negative, the unmasked characters will be at the start of the string. Omit the third argument, mask, to use a default character of '*' for the mask.

const mask = (cc, num = 4, mask = '*') =>
+  ('' + cc).slice(0, -num).replace(/./g, mask) + ('' + cc).slice(-num);
+
mask(1234567890); // '******7890'
+mask(1234567890, 3); // '*******890'
+mask(1234567890, 4, '$'); // '$$$$$$7890'
+mask(1234567890, -4, '$'); // '1234$$$$$$'
 

palindrome

Returns true if the given string is a palindrome, false otherwise.

Convert string toLowerCase() and use replace() to remove non-alphanumeric characters from it. Then, split('') into individual characters, reverse(), join('') and compare to the original, unreversed string, after converting it tolowerCase().

const palindrome = str => {
   const s = str.toLowerCase().replace(/[\W_]/g, '');
   return (
@@ -806,6 +855,11 @@ extendHex('05a'); // '#0055aa'
 

getType

Returns the native type of a value.

Returns lowercased constructor name of value, "undefined" or "null" if value is undefined or null

const getType = v =>
   v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();
 
getType(new Set([1, 2, 3])); // "set"
+

getURLParameters

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. Pass location.search as the argument to apply to the current url.

const getURLParameters = url =>
+  url
+    .match(/([^?=&]+)(=([^&]*))/g)
+    .reduce((a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {});
+
getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'}
 

hexToRGB

Converts a color code to a rgb() or rgba() string if alpha value is provided.

Use bitwise right-shift operator and mask bits with & (and) operator to convert a hexadecimal color code (with or without prefixed with #) to a string with the RGB values. If it's 3-digit color code, first convert to 6-digit version. If an alpha value is provided alongside 6-digit hex, give rgba() string in return.

const hexToRGB = hex => {
   let alpha = false,
     h = hex.slice(hex.startsWith('#') ? 1 : 0);
@@ -831,6 +885,15 @@ hexToRGB('#fff'); // 'rgb(255, 255, 255)'
 

isArray

Checks if the given argument is an array.

Use Array.isArray() to check if a value is classified as an array.

const isArray = val => !!val && Array.isArray(val);
 
isArray(null); // false
 isArray([1]); // true
+

isArrayLike

Checks if the provided argument is array-like (i.e. is iterable).

Use the spread operator (...) to check if the provided argument is iterable inside a try... catch block and the comma operator (,) to return the appropriate value.


+
+const isArrayLike = val =>
+  try {return [...val], true; }
+  catch (e)  { return false; }
+};
+
isArrayLike(document.querySelectorAll('.className')); // true
+isArrayLike('abc'); // true
+isArrayLike(null); // false
 

isBoolean

Checks if the given argument is a native boolean element.

Use typeof to check if a value is classified as a boolean primitive.

const isBoolean = val => typeof val === 'boolean';
 
isBoolean(null); // false
 isBoolean(false); // true
@@ -843,12 +906,43 @@ isNull('null'); // false
 

isNumber

Checks if the given argument is a number.

Use typeof to check if a value is classified as a number primitive.

const isNumber = val => typeof val === 'number';
 
isNumber('1'); // false
 isNumber(1); // true
+

isPrimitive

Returns a boolean determining if the supplied value is primitive or not.

Use Array.includes() on an array of type strings which are not primitive, supplying the type using typeof. Since typeof null evaluates to 'object', it needs to be directly compared.

const isPrimitive = val => !['object', 'function'].includes(typeof val) || val === null;
+
isPrimitive(window.someNonExistentProperty); // true
+isPrimitive(null); // true
+isPrimitive(50); // true
+isPrimitive('Hello!'); // true
+isPrimitive(false); // true
+isPrimitive(Symbol()); // true
+isPrimitive([]); // false
+isPrimitive(new String('Hello!')); // false
+

isPromiseLike

Returns true if an object looks like a Promise, false otherwise.

Check if the object is not null, its typeof matches either object or function and if it has a .then property, which is also a function.

const isPromiseLike = obj =>
+  obj !== null &&
+  (typeof obj === 'object' || typeof obj === 'function') &&
+  typeof obj.then === 'function';
+
isPromiseLike({
+  then: function() {
+    return '';
+  }
+}); // true
+isPromiseLike(null); // false
+isPromiseLike({}); // false
 

isString

Checks if the given argument is a string.

Use typeof to check if a value is classified as a string primitive.

const isString = val => typeof val === 'string';
 
isString(10); // false
 isString('10'); // true
 

isSymbol

Checks if the given argument is a symbol.

Use typeof to check if a value is classified as a symbol primitive.

const isSymbol = val => typeof val === 'symbol';
 
isSymbol('x'); // false
 isSymbol(Symbol('x')); // true
+

isValidJSON

Checks if the provided argument is a valid JSON.

Use JSON.parse() and a try... catch block to check if the provided argument is a valid JSON.

const isValidJSON = obj => {
+  try {
+    JSON.parse(obj);
+    return true;
+  } catch (e) {
+    return false;
+  }
+};
+
isValidJSON('{"name":"Adam","age":20}'); // true
+isValidJSON('{"name":"Adam",age:"20"}'); // false
+isValidJSON(null); // true
 

randomHexColorCode

Generates a random hexadecimal color code.

Use Math.random to generate a random 24-bit(6x4bits) hexadecimal number. Use bit shifting and then convert it to an hexadecimal String using toString(16).

const randomHexColorCode = () => {
   let n = ((Math.random() * 0xfffff) | 0).toString(16);
   return '#' + (n.length !== 6 ? ((Math.random() * 0xf) | 0).toString(16) + n : n);
diff --git a/docs/mini.css b/docs/mini.css
index 501ee5dda..db18d9467 100644
--- a/docs/mini.css
+++ b/docs/mini.css
@@ -1 +1 @@
-:root{--fore-color:#111;--secondary-fore-color:#444;--back-color:#f8f8f8;--secondary-back-color:#f0f0f0;--blockquote-color:#f57c00;--pre-color:#1565c0;--border-color:#aaa;--secondary-border-color:#ddd;--heading-ratio:1.19;--universal-margin:.5rem;--universal-padding:.5rem;--universal-border-radius:.125rem;--a-link-color:#0277bd;--a-visited-color:#01579b}html{font-size:16px}a,b,del,em,i,ins,q,span,strong,u{font-size:1em}html,*{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Helvetica, sans-serif;line-height:1.5;-webkit-text-size-adjust:100%}*{font-size:1rem}body{margin:0;color:var(--fore-color);background:var(--back-color)}details{display:block}summary{display:list-item}abbr[title]{border-bottom:none;text-decoration:underline dotted}input{overflow:visible}img{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{line-height:1.2;margin:calc(1.5 * var(--universal-margin)) var(--universal-margin);font-weight:500}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:var(--secondary-fore-color);display:block;margin-top:-.25rem}h1{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio))}h2{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio))}h3{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio))}h4{font-size:calc(1rem * var(--heading-ratio))}h5{font-size:1rem}h6{font-size:calc(1rem / var(--heading-ratio))}p{margin:var(--universal-margin)}ol,ul{margin:var(--universal-margin);padding-left:calc(2 * var(--universal-margin))}b,strong{font-weight:700}hr{box-sizing:content-box;border:0;line-height:1.25em;margin:var(--universal-margin);height:.0625rem;background:linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent)}blockquote{display:block;position:relative;font-style:italic;color:var(--secondary-fore-color);margin:var(--universal-margin);padding:calc(3 * var(--universal-padding));border:.0625rem solid var(--secondary-border-color);border-left:.375rem solid var(--blockquote-color);border-radius:0 var(--universal-border-radius) var(--universal-border-radius) 0}blockquote:before{position:absolute;top:calc(0rem - var(--universal-padding));left:0;font-family:sans-serif;font-size:3rem;font-weight:700;content:"\201c";color:var(--blockquote-color)}blockquote[cite]:after{font-style:normal;font-size:.75em;font-weight:700;content:"\a—  " attr(cite);white-space:pre}code,kbd,pre,samp{font-family:Menlo, Consolas, monospace;font-size:.85em}code{background:var(--secondary-back-color);border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}kbd{background:var(--fore-color);color:var(--back-color);border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}pre{overflow:auto;background:var(--secondary-back-color);padding:calc(1.5 * var(--universal-padding));margin:var(--universal-margin);border:.0625rem solid var(--secondary-border-color);border-left:.25rem solid var(--pre-color);border-radius:0 var(--universal-border-radius) var(--universal-border-radius) 0}sup,sub,code,kbd{line-height:0;position:relative;vertical-align:baseline}small,sup,sub,figcaption{font-size:.75em}sup{top:-.5em}sub{bottom:-.25em}figure{margin:var(--universal-margin)}figcaption{color:var(--secondary-fore-color)}a{text-decoration:none}a:link{color:var(--a-link-color)}a:visited{color:var(--a-visited-color)}a:hover,a:focus{text-decoration:underline}.container{margin:0 auto;padding:0 calc(1.5 * var(--universal-padding))}.row{box-sizing:border-box;display:flex;flex:0 1 auto;flex-flow:row wrap}.col-sm,[class^='col-sm-'],[class^='col-sm-offset-'],.row[class*='cols-sm-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-sm,.row.cols-sm>*{max-width:100%;flex-grow:1;flex-basis:0}.col-sm-1,.row.cols-sm-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-sm-offset-0{margin-left:0}.col-sm-2,.row.cols-sm-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-sm-offset-1{margin-left:8.33333%}.col-sm-3,.row.cols-sm-3>*{max-width:25%;flex-basis:25%}.col-sm-offset-2{margin-left:16.66667%}.col-sm-4,.row.cols-sm-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-sm-offset-3{margin-left:25%}.col-sm-5,.row.cols-sm-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-sm-offset-4{margin-left:33.33333%}.col-sm-6,.row.cols-sm-6>*{max-width:50%;flex-basis:50%}.col-sm-offset-5{margin-left:41.66667%}.col-sm-7,.row.cols-sm-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-sm-offset-6{margin-left:50%}.col-sm-8,.row.cols-sm-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-sm-offset-7{margin-left:58.33333%}.col-sm-9,.row.cols-sm-9>*{max-width:75%;flex-basis:75%}.col-sm-offset-8{margin-left:66.66667%}.col-sm-10,.row.cols-sm-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-sm-offset-9{margin-left:75%}.col-sm-11,.row.cols-sm-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-sm-offset-10{margin-left:83.33333%}.col-sm-12,.row.cols-sm-12>*{max-width:100%;flex-basis:100%}.col-sm-offset-11{margin-left:91.66667%}.col-sm-normal{order:initial}.col-sm-first{order:-999}.col-sm-last{order:999}@media screen and (min-width: 768px){.col-md,[class^='col-md-'],[class^='col-md-offset-'],.row[class*='cols-md-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-md,.row.cols-md>*{max-width:100%;flex-grow:1;flex-basis:0}.col-md-1,.row.cols-md-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-md-offset-0{margin-left:0}.col-md-2,.row.cols-md-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-md-offset-1{margin-left:8.33333%}.col-md-3,.row.cols-md-3>*{max-width:25%;flex-basis:25%}.col-md-offset-2{margin-left:16.66667%}.col-md-4,.row.cols-md-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-md-offset-3{margin-left:25%}.col-md-5,.row.cols-md-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-md-offset-4{margin-left:33.33333%}.col-md-6,.row.cols-md-6>*{max-width:50%;flex-basis:50%}.col-md-offset-5{margin-left:41.66667%}.col-md-7,.row.cols-md-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-md-offset-6{margin-left:50%}.col-md-8,.row.cols-md-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-md-offset-7{margin-left:58.33333%}.col-md-9,.row.cols-md-9>*{max-width:75%;flex-basis:75%}.col-md-offset-8{margin-left:66.66667%}.col-md-10,.row.cols-md-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-md-offset-9{margin-left:75%}.col-md-11,.row.cols-md-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-md-offset-10{margin-left:83.33333%}.col-md-12,.row.cols-md-12>*{max-width:100%;flex-basis:100%}.col-md-offset-11{margin-left:91.66667%}.col-md-normal{order:initial}.col-md-first{order:-999}.col-md-last{order:999}}@media screen and (min-width: 1280px){.col-lg,[class^='col-lg-'],[class^='col-lg-offset-'],.row[class*='cols-lg-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-lg,.row.cols-lg>*{max-width:100%;flex-grow:1;flex-basis:0}.col-lg-1,.row.cols-lg-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-lg-offset-0{margin-left:0}.col-lg-2,.row.cols-lg-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-lg-offset-1{margin-left:8.33333%}.col-lg-3,.row.cols-lg-3>*{max-width:25%;flex-basis:25%}.col-lg-offset-2{margin-left:16.66667%}.col-lg-4,.row.cols-lg-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-lg-offset-3{margin-left:25%}.col-lg-5,.row.cols-lg-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-lg-offset-4{margin-left:33.33333%}.col-lg-6,.row.cols-lg-6>*{max-width:50%;flex-basis:50%}.col-lg-offset-5{margin-left:41.66667%}.col-lg-7,.row.cols-lg-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-lg-offset-6{margin-left:50%}.col-lg-8,.row.cols-lg-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-lg-offset-7{margin-left:58.33333%}.col-lg-9,.row.cols-lg-9>*{max-width:75%;flex-basis:75%}.col-lg-offset-8{margin-left:66.66667%}.col-lg-10,.row.cols-lg-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-lg-offset-9{margin-left:75%}.col-lg-11,.row.cols-lg-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-lg-offset-10{margin-left:83.33333%}.col-lg-12,.row.cols-lg-12>*{max-width:100%;flex-basis:100%}.col-lg-offset-11{margin-left:91.66667%}.col-lg-normal{order:initial}.col-lg-first{order:-999}.col-lg-last{order:999}}:root{--card-back-color:#f8f8f8;--card-fore-color:#111;--card-border-color:#ddd}.card{display:flex;flex-direction:column;justify-content:space-between;align-self:center;position:relative;width:100%;background:var(--card-back-color);color:var(--card-fore-color);border:.0625rem solid var(--card-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin);overflow:hidden}@media screen and (min-width: 320px){.card{max-width:320px}}.card>.section{background:var(--card-back-color);color:var(--card-fore-color);box-sizing:border-box;margin:0;border:0;border-radius:0;border-bottom:.0625rem solid var(--card-border-color);padding:var(--universal-padding);width:100%}.card>.section.media{height:200px;padding:0;-o-object-fit:cover;object-fit:cover}.card>.section:last-child{border-bottom:0}@media screen and (min-width: 240px){.card.small{max-width:240px}}@media screen and (min-width: 480px){.card.large{max-width:480px}}.card.fluid{max-width:100%;width:auto}.card.warning{--card-back-color:#ffca28;--card-border-color:#e8b825}.card.error{--card-back-color:#b71c1c;--card-fore-color:#f8f8f8;--card-border-color:#a71a1a}.card>.section.dark{--card-back-color:#e0e0e0}.card>.section.double-padded{padding:calc(1.5 * var(--universal-padding))}:root{--form-back-color:#f0f0f0;--form-fore-color:#111;--form-border-color:#ddd;--input-back-color:#f8f8f8;--input-fore-color:#111;--input-border-color:#ddd;--input-focus-color:#0288d1;--input-invalid-color:#d32f2f;--button-back-color:#e2e2e2;--button-hover-back-color:#dcdcdc;--button-fore-color:#212121;--button-border-color:transparent;--button-hover-border-color:transparent;--button-group-border-color:rgba(124,124,124,0.54)}form{background:var(--form-back-color);color:var(--form-fore-color);border:.0625rem solid var(--form-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin);padding:calc(2 * var(--universal-padding)) var(--universal-padding)}fieldset{border:.0625rem solid var(--form-border-color);border-radius:var(--universal-border-radius);margin:calc(var(--universal-margin) / 4);padding:var(--universal-padding)}legend{box-sizing:border-box;display:table;max-width:100%;white-space:normal;font-weight:700;padding:calc(var(--universal-padding) / 2)}label{padding:calc(var(--universal-padding) / 2) var(--universal-padding)}.input-group{display:inline-block}.input-group.fluid{display:flex;align-items:center;justify-content:center}.input-group.fluid>input{max-width:100%;flex-grow:1;flex-basis:0px}@media screen and (max-width: 767px){.input-group.fluid{align-items:stretch;flex-direction:column}}.input-group.vertical{display:flex;align-items:stretch;flex-direction:column}.input-group.vertical>input{max-width:100%;flex-grow:1;flex-basis:0px}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input:not([type]),[type="text"],[type="email"],[type="number"],[type="search"],[type="password"],[type="url"],[type="tel"],[type="checkbox"],[type="radio"],textarea,select{box-sizing:border-box;background:var(--input-back-color);color:var(--input-fore-color);border:.0625rem solid var(--input-border-color);border-radius:var(--universal-border-radius);margin:calc(var(--universal-margin) / 2);padding:var(--universal-padding) calc(1.5 * var(--universal-padding))}input:not([type="button"]):not([type="submit"]):not([type="reset"]):hover,input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus,textarea:hover,textarea:focus,select:hover,select:focus{border-color:var(--input-focus-color);box-shadow:none}input:not([type="button"]):not([type="submit"]):not([type="reset"]):invalid,input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus:invalid,textarea:invalid,textarea:focus:invalid,select:invalid,select:focus:invalid{border-color:var(--input-invalid-color);box-shadow:none}input:not([type="button"]):not([type="submit"]):not([type="reset"])[readonly],textarea[readonly],select[readonly]{background:var(--secondary-back-color)}select{max-width:100%}option{overflow:hidden;text-overflow:ellipsis}[type="checkbox"],[type="radio"]{-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative;height:calc(1rem + var(--universal-padding) / 2);width:calc(1rem + var(--universal-padding) / 2);vertical-align:text-bottom;padding:0;flex-basis:calc(1rem + var(--universal-padding) / 2) !important;flex-grow:0 !important}[type="checkbox"]:checked:before,[type="radio"]:checked:before{position:absolute}[type="checkbox"]:checked:before{content:'\2713';font-family:sans-serif;font-size:calc(1rem + var(--universal-padding) / 2);top:calc(0rem - var(--universal-padding));left:calc(var(--universal-padding) / 4)}[type="radio"]{border-radius:100%}[type="radio"]:checked:before{border-radius:100%;content:'';top:calc(.0625rem + var(--universal-padding) / 2);left:calc(.0625rem + var(--universal-padding) / 2);background:var(--input-fore-color);width:0.5rem;height:0.5rem}:placeholder-shown{color:var(--input-fore-color)}::-ms-placeholder{color:var(--input-fore-color);opacity:0.54}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button{overflow:visible;text-transform:none}button,[type="button"],[type="submit"],[type="reset"],a.button,label.button,.button,a[role="button"],label[role="button"],[role="button"]{display:inline-block;background:var(--button-back-color);color:var(--button-fore-color);border:.0625rem solid var(--button-border-color);border-radius:var(--universal-border-radius);padding:var(--universal-padding) calc(1.5 * var(--universal-padding));margin:var(--universal-margin);text-decoration:none;cursor:pointer;transition:background 0.3s}button:hover,button:focus,[type="button"]:hover,[type="button"]:focus,[type="submit"]:hover,[type="submit"]:focus,[type="reset"]:hover,[type="reset"]:focus,a.button:hover,a.button:focus,label.button:hover,label.button:focus,.button:hover,.button:focus,a[role="button"]:hover,a[role="button"]:focus,label[role="button"]:hover,label[role="button"]:focus,[role="button"]:hover,[role="button"]:focus{background:var(--button-hover-back-color);border-color:var(--button-hover-border-color)}input:disabled,input[disabled],textarea:disabled,textarea[disabled],select:disabled,select[disabled],button:disabled,button[disabled],.button:disabled,.button[disabled],[role="button"]:disabled,[role="button"][disabled]{cursor:not-allowed;opacity:.75}.button-group{display:flex;border:.0625rem solid var(--button-group-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin)}.button-group>button,.button-group [type="button"],.button-group>[type="submit"],.button-group>[type="reset"],.button-group>.button,.button-group>[role="button"]{margin:0;max-width:100%;flex:1 1 auto;text-align:center;border:0;border-radius:0;box-shadow:none}.button-group>:not(:first-child){border-left:.0625rem solid var(--button-group-border-color)}@media screen and (max-width: 767px){.button-group{flex-direction:column}.button-group>:not(:first-child){border:0;border-top:.0625rem solid var(--button-group-border-color)}}button.primary,[type="button"].primary,[type="submit"].primary,[type="reset"].primary,.button.primary,[role="button"].primary{--button-back-color:#1976d2;--button-fore-color:#f8f8f8}button.primary:hover,button.primary:focus,[type="button"].primary:hover,[type="button"].primary:focus,[type="submit"].primary:hover,[type="submit"].primary:focus,[type="reset"].primary:hover,[type="reset"].primary:focus,.button.primary:hover,.button.primary:focus,[role="button"].primary:hover,[role="button"].primary:focus{--button-hover-back-color:#1565c0}button.secondary,[type="button"].secondary,[type="submit"].secondary,[type="reset"].secondary,.button.secondary,[role="button"].secondary{--button-back-color:#d32f2f;--button-fore-color:#f8f8f8}button.secondary:hover,button.secondary:focus,[type="button"].secondary:hover,[type="button"].secondary:focus,[type="submit"].secondary:hover,[type="submit"].secondary:focus,[type="reset"].secondary:hover,[type="reset"].secondary:focus,.button.secondary:hover,.button.secondary:focus,[role="button"].secondary:hover,[role="button"].secondary:focus{--button-hover-back-color:#c62828}button.tertiary,[type="button"].tertiary,[type="submit"].tertiary,[type="reset"].tertiary,.button.tertiary,[role="button"].tertiary{--button-back-color:#308732;--button-fore-color:#f8f8f8}button.tertiary:hover,button.tertiary:focus,[type="button"].tertiary:hover,[type="button"].tertiary:focus,[type="submit"].tertiary:hover,[type="submit"].tertiary:focus,[type="reset"].tertiary:hover,[type="reset"].tertiary:focus,.button.tertiary:hover,.button.tertiary:focus,[role="button"].tertiary:hover,[role="button"].tertiary:focus{--button-hover-back-color:#277529}button.inverse,[type="button"].inverse,[type="submit"].inverse,[type="reset"].inverse,.button.inverse,[role="button"].inverse{--button-back-color:#212121;--button-fore-color:#f8f8f8}button.inverse:hover,button.inverse:focus,[type="button"].inverse:hover,[type="button"].inverse:focus,[type="submit"].inverse:hover,[type="submit"].inverse:focus,[type="reset"].inverse:hover,[type="reset"].inverse:focus,.button.inverse:hover,.button.inverse:focus,[role="button"].inverse:hover,[role="button"].inverse:focus{--button-hover-back-color:#111}button.small,[type="button"].small,[type="submit"].small,[type="reset"].small,.button.small,[role="button"].small{padding:calc(0.5 * var(--universal-padding)) calc(0.75 * var(--universal-padding));margin:var(--universal-margin)}button.large,[type="button"].large,[type="submit"].large,[type="reset"].large,.button.large,[role="button"].large{padding:calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding));margin:var(--universal-margin)}:root{--header-back-color:#f8f8f8;--header-hover-back-color:#f0f0f0;--header-fore-color:#444;--header-border-color:#ddd;--nav-back-color:#f8f8f8;--nav-hover-back-color:#f0f0f0;--nav-fore-color:#444;--nav-border-color:#ddd;--nav-link-color:#0277bd;--footer-fore-color:#444;--footer-back-color:#f8f8f8;--footer-border-color:#ddd;--footer-link-color:#0277bd;--drawer-back-color:#f8f8f8;--drawer-hover-back-color:#f0f0f0;--drawer-border-color:#ddd;--drawer-close-color:#444}header{height:3.1875rem;background:var(--header-back-color);color:var(--header-fore-color);border-bottom:.0625rem solid var(--header-border-color);padding:calc(var(--universal-padding) / 4) 0;white-space:nowrap;overflow-x:auto;overflow-y:hidden}header.row{box-sizing:content-box}header .logo{color:var(--header-fore-color);font-size:1.75rem;padding:var(--universal-padding) calc(2 * var(--universal-padding));text-decoration:none}header button,header [type="button"],header .button,header [role="button"]{box-sizing:border-box;position:relative;top:calc(0rem - var(--universal-padding) / 4);height:calc(3.1875rem + var(--universal-padding) / 2);background:var(--header-back-color);line-height:calc(3.1875rem - var(--universal-padding) * 1.5);text-align:center;color:var(--header-fore-color);border:0;border-radius:0;margin:0;text-transform:uppercase}header button:hover,header button:focus,header [type="button"]:hover,header [type="button"]:focus,header .button:hover,header .button:focus,header [role="button"]:hover,header [role="button"]:focus{background:var(--header-hover-back-color)}nav{background:var(--nav-back-color);color:var(--nav-fore-color);border:.0625rem solid var(--nav-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin)}nav *{padding:var(--universal-padding) calc(1.5 * var(--universal-padding))}nav a,nav a:visited{display:block;color:var(--nav-link-color);border-radius:var(--universal-border-radius);transition:background 0.3s}nav a:hover,nav a:focus,nav a:visited:hover,nav a:visited:focus{text-decoration:none;background:var(--nav-hover-back-color)}nav .sublink-1{position:relative;margin-left:calc(2 * var(--universal-padding))}nav .sublink-1:before{position:absolute;left:calc(var(--universal-padding) - 1 * var(--universal-padding));top:-.0625rem;content:'';height:100%;border:.0625rem solid var(--nav-border-color);border-left:0}nav .sublink-2{position:relative;margin-left:calc(4 * var(--universal-padding))}nav .sublink-2:before{position:absolute;left:calc(var(--universal-padding) - 3 * var(--universal-padding));top:-.0625rem;content:'';height:100%;border:.0625rem solid var(--nav-border-color);border-left:0}footer{background:var(--footer-back-color);color:var(--footer-fore-color);border-top:.0625rem solid var(--footer-border-color);padding:calc(2 * var(--universal-padding)) var(--universal-padding);font-size:.875rem}footer a,footer a:visited{color:var(--footer-link-color)}header.sticky{position:-webkit-sticky;position:sticky;z-index:1101;top:0}footer.sticky{position:-webkit-sticky;position:sticky;z-index:1101;bottom:0}.drawer-toggle:before{display:inline-block;position:relative;vertical-align:bottom;content:'\00a0\2261\00a0';font-family:sans-serif;font-size:1.5em}@media screen and (min-width: 768px){.drawer-toggle:not(.persistent){display:none}}[type="checkbox"].drawer{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}[type="checkbox"].drawer+*{display:block;box-sizing:border-box;position:fixed;top:0;width:320px;height:100vh;overflow-y:auto;background:var(--drawer-back-color);border:.0625rem solid var(--drawer-border-color);border-radius:0;margin:0;z-index:1110;left:-320px;transition:left 0.3s}[type="checkbox"].drawer+* .drawer-close{position:absolute;top:var(--universal-margin);right:var(--universal-margin);z-index:1111;width:2rem;height:2rem;border-radius:var(--universal-border-radius);padding:var(--universal-padding);margin:0;cursor:pointer;transition:background 0.3s}[type="checkbox"].drawer+* .drawer-close:before{display:block;content:'\00D7';color:var(--drawer-close-color);position:relative;font-family:sans-serif;font-size:2rem;line-height:1;text-align:center}[type="checkbox"].drawer+* .drawer-close:hover,[type="checkbox"].drawer+* .drawer-close:focus{background:var(--drawer-hover-back-color)}@media screen and (max-width: 320px){[type="checkbox"].drawer+*{width:100%}}[type="checkbox"].drawer:checked+*{left:0}@media screen and (min-width: 768px){[type="checkbox"].drawer:not(.persistent)+*{position:static;height:100%;z-index:1100}[type="checkbox"].drawer:not(.persistent)+* .drawer-close{display:none}}:root{--mark-back-color:#0277bd;--mark-fore-color:#fafafa}mark{background:var(--mark-back-color);color:var(--mark-fore-color);font-size:.95em;line-height:1em;border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}mark.inline-block{display:inline-block;font-size:1em;line-height:1.5;padding:calc(var(--universal-padding) / 2) var(--universal-padding)}:root{--toast-back-color:#424242;--toast-fore-color:#fafafa}.toast{position:fixed;bottom:calc(var(--universal-margin) * 3);left:50%;transform:translate(-50%, -50%);z-index:1111;color:var(--toast-fore-color);background:var(--toast-back-color);border-radius:calc(var(--universal-border-radius) * 16);padding:var(--universal-padding) calc(var(--universal-padding) * 3)}:root{--tooltip-back-color:#212121;--tooltip-fore-color:#fafafa}.tooltip{position:relative;display:inline-block}.tooltip:before,.tooltip:after{position:absolute;opacity:0;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%);transition:all 0.3s;z-index:1010;left:50%}.tooltip:not(.bottom):before,.tooltip:not(.bottom):after{bottom:75%}.tooltip.bottom:before,.tooltip.bottom:after{top:75%}.tooltip:hover:before,.tooltip:hover:after,.tooltip:focus:before,.tooltip:focus:after{opacity:1;clip:auto;-webkit-clip-path:inset(0%);clip-path:inset(0%)}.tooltip:before{content:'';background:transparent;border:var(--universal-margin) solid transparent;left:calc(50% - var(--universal-margin))}.tooltip:not(.bottom):before{border-top-color:#212121}.tooltip.bottom:before{border-bottom-color:#212121}.tooltip:after{content:attr(aria-label);color:var(--tooltip-fore-color);background:var(--tooltip-back-color);border-radius:var(--universal-border-radius);padding:var(--universal-padding);white-space:nowrap;transform:translateX(-50%)}.tooltip:not(.bottom):after{margin-bottom:calc(2 * var(--universal-margin))}.tooltip.bottom:after{margin-top:calc(2 * var(--universal-margin))}:root{--modal-overlay-color:rgba(0,0,0,0.45);--modal-close-color:#444;--modal-close-hover-color:#f0f0f0}[type="checkbox"].modal{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}[type="checkbox"].modal+div{position:fixed;top:0;left:0;display:none;width:100vw;height:100vh;background:var(--modal-overlay-color)}[type="checkbox"].modal+div .card{margin:0 auto;max-height:50vh;overflow:auto}[type="checkbox"].modal+div .card .modal-close{position:absolute;top:0;right:0;width:1.75rem;height:1.75rem;border-radius:var(--universal-border-radius);padding:var(--universal-padding);margin:0;cursor:pointer;transition:background 0.3s}[type="checkbox"].modal+div .card .modal-close:before{display:block;content:'\00D7';color:var(--modal-close-color);position:relative;font-family:sans-serif;font-size:1.75rem;line-height:1;text-align:center}[type="checkbox"].modal+div .card .modal-close:hover,[type="checkbox"].modal+div .card .modal-close:focus{background:var(--modal-close-hover-color)}[type="checkbox"].modal:checked+div{display:flex;flex:0 1 auto;z-index:1200}[type="checkbox"].modal:checked+div .card .modal-close{z-index:1211}:root{--collapse-label-back-color:#e8e8e8;--collapse-label-fore-color:#212121;--collapse-label-hover-back-color:#f0f0f0;--collapse-selected-label-back-color:#ececec;--collapse-border-color:#ddd;--collapse-content-back-color:#fafafa;--collapse-selected-label-border-color:#0277bd}.collapse{width:calc(100% - 2 * var(--universal-margin));opacity:1;display:flex;flex-direction:column;margin:var(--universal-margin);border-radius:var(--universal-border-radius)}.collapse>[type="radio"],.collapse>[type="checkbox"]{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}.collapse>label{flex-grow:1;display:inline-block;height:1.5rem;cursor:pointer;transition:background 0.3s;color:var(--collapse-label-fore-color);background:var(--collapse-label-back-color);border:.0625rem solid var(--collapse-border-color);padding:calc(1.5 * var(--universal-padding))}.collapse>label:hover,.collapse>label:focus{background:var(--collapse-label-hover-back-color)}.collapse>label+div{flex-basis:auto;height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%);transition:max-height 0.3s;max-height:1px}.collapse>:checked+label{background:var(--collapse-selected-label-back-color);border-bottom-color:var(--collapse-selected-label-border-color)}.collapse>:checked+label+div{box-sizing:border-box;position:relative;width:100%;height:auto;overflow:auto;margin:0;background:var(--collapse-content-back-color);border:.0625rem solid var(--collapse-border-color);border-top:0;padding:var(--universal-padding);clip:auto;-webkit-clip-path:inset(0%);clip-path:inset(0%);max-height:400px}.collapse>label:not(:first-of-type){border-top:0}.collapse>label:first-of-type{border-radius:var(--universal-border-radius) var(--universal-border-radius) 0 0}.collapse>label:last-of-type{border-radius:0 0 var(--universal-border-radius) var(--universal-border-radius)}.collapse>:checked:last-of-type+label{border-radius:0}.collapse>:checked:last-of-type+label+div{border-radius:0 0 var(--universal-border-radius) var(--universal-border-radius)}mark.secondary{--mark-back-color:#d32f2f}mark.tertiary{--mark-back-color:#308732}mark.tag{padding:calc(var(--universal-padding)/2) var(--universal-padding);border-radius:1em}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}code{transform:scale(1)}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;text-align:center}header h1.logo a{text-decoration:none;color:#111}header #title{position:relative;top:-1rem}@media screen and (max-width: 500px){header #title{font-size:1rem;display:block}}header h1 small{display:block;font-size:0.875rem;font-style:italic;color:#888;margin-top:-0.8rem}@media screen and (max-width: 768px){header h1 small{font-size:0.75rem}}@media screen and (max-width: 600px){header h1 small{font-size:0.625rem}}@media screen and (max-width: 500px){header h1 small{font-size:0.5rem;margin-top:-1.2rem}}label#menu-toggle{position:absolute;left:0.5rem;top:0.5rem;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}div.collapse{margin:0;width:100%}div.collapse>label{border-top-left-radius:var(--universal-border-radius) !important;border-top-right-radius:var(--universal-border-radius) !important}div.collapse>div{padding:0 !important}div.collapse>div>pre{margin:0;border:0}button.primary.clipboard-copy{width:100%;margin-left:0}button.primary.clipboard-copy>svg{vertical-align:bottom}
+:root{--fore-color:#111;--secondary-fore-color:#444;--back-color:#f8f8f8;--secondary-back-color:#f0f0f0;--blockquote-color:#f57c00;--pre-color:#1565c0;--border-color:#aaa;--secondary-border-color:#ddd;--heading-ratio:1.19;--universal-margin:.5rem;--universal-padding:.5rem;--universal-border-radius:.125rem;--a-link-color:#0277bd;--a-visited-color:#01579b}html{font-size:16px}a,b,del,em,i,ins,q,span,strong,u{font-size:1em}html,*{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Helvetica, sans-serif;line-height:1.5;-webkit-text-size-adjust:100%}*{font-size:1rem}body{margin:0;color:var(--fore-color);background:var(--back-color)}details{display:block}summary{display:list-item}abbr[title]{border-bottom:none;text-decoration:underline dotted}input{overflow:visible}img{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{line-height:1.2;margin:calc(1.5 * var(--universal-margin)) var(--universal-margin);font-weight:500}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:var(--secondary-fore-color);display:block;margin-top:-.25rem}h1{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio))}h2{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio) * var(--heading-ratio))}h3{font-size:calc(1rem * var(--heading-ratio) * var(--heading-ratio))}h4{font-size:calc(1rem * var(--heading-ratio))}h5{font-size:1rem}h6{font-size:calc(1rem / var(--heading-ratio))}p{margin:var(--universal-margin)}ol,ul{margin:var(--universal-margin);padding-left:calc(2 * var(--universal-margin))}b,strong{font-weight:700}hr{box-sizing:content-box;border:0;line-height:1.25em;margin:var(--universal-margin);height:.0625rem;background:linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent)}blockquote{display:block;position:relative;font-style:italic;color:var(--secondary-fore-color);margin:var(--universal-margin);padding:calc(3 * var(--universal-padding));border:.0625rem solid var(--secondary-border-color);border-left:.375rem solid var(--blockquote-color);border-radius:0 var(--universal-border-radius) var(--universal-border-radius) 0}blockquote:before{position:absolute;top:calc(0rem - var(--universal-padding));left:0;font-family:sans-serif;font-size:3rem;font-weight:700;content:"\201c";color:var(--blockquote-color)}blockquote[cite]:after{font-style:normal;font-size:.75em;font-weight:700;content:"\a—  " attr(cite);white-space:pre}code,kbd,pre,samp{font-family:Menlo, Consolas, monospace;font-size:.85em}code{background:var(--secondary-back-color);border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}kbd{background:var(--fore-color);color:var(--back-color);border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}pre{overflow:auto;background:var(--secondary-back-color);padding:calc(1.5 * var(--universal-padding));margin:var(--universal-margin);border:.0625rem solid var(--secondary-border-color);border-left:.25rem solid var(--pre-color);border-radius:0 var(--universal-border-radius) var(--universal-border-radius) 0}sup,sub,code,kbd{line-height:0;position:relative;vertical-align:baseline}small,sup,sub,figcaption{font-size:.75em}sup{top:-.5em}sub{bottom:-.25em}figure{margin:var(--universal-margin)}figcaption{color:var(--secondary-fore-color)}a{text-decoration:none}a:link{color:var(--a-link-color)}a:visited{color:var(--a-visited-color)}a:hover,a:focus{text-decoration:underline}.container{margin:0 auto;padding:0 calc(1.5 * var(--universal-padding))}.row{box-sizing:border-box;display:flex;flex:0 1 auto;flex-flow:row wrap}.col-sm,[class^='col-sm-'],[class^='col-sm-offset-'],.row[class*='cols-sm-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-sm,.row.cols-sm>*{max-width:100%;flex-grow:1;flex-basis:0}.col-sm-1,.row.cols-sm-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-sm-offset-0{margin-left:0}.col-sm-2,.row.cols-sm-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-sm-offset-1{margin-left:8.33333%}.col-sm-3,.row.cols-sm-3>*{max-width:25%;flex-basis:25%}.col-sm-offset-2{margin-left:16.66667%}.col-sm-4,.row.cols-sm-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-sm-offset-3{margin-left:25%}.col-sm-5,.row.cols-sm-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-sm-offset-4{margin-left:33.33333%}.col-sm-6,.row.cols-sm-6>*{max-width:50%;flex-basis:50%}.col-sm-offset-5{margin-left:41.66667%}.col-sm-7,.row.cols-sm-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-sm-offset-6{margin-left:50%}.col-sm-8,.row.cols-sm-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-sm-offset-7{margin-left:58.33333%}.col-sm-9,.row.cols-sm-9>*{max-width:75%;flex-basis:75%}.col-sm-offset-8{margin-left:66.66667%}.col-sm-10,.row.cols-sm-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-sm-offset-9{margin-left:75%}.col-sm-11,.row.cols-sm-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-sm-offset-10{margin-left:83.33333%}.col-sm-12,.row.cols-sm-12>*{max-width:100%;flex-basis:100%}.col-sm-offset-11{margin-left:91.66667%}.col-sm-normal{order:initial}.col-sm-first{order:-999}.col-sm-last{order:999}@media screen and (min-width: 768px){.col-md,[class^='col-md-'],[class^='col-md-offset-'],.row[class*='cols-md-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-md,.row.cols-md>*{max-width:100%;flex-grow:1;flex-basis:0}.col-md-1,.row.cols-md-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-md-offset-0{margin-left:0}.col-md-2,.row.cols-md-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-md-offset-1{margin-left:8.33333%}.col-md-3,.row.cols-md-3>*{max-width:25%;flex-basis:25%}.col-md-offset-2{margin-left:16.66667%}.col-md-4,.row.cols-md-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-md-offset-3{margin-left:25%}.col-md-5,.row.cols-md-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-md-offset-4{margin-left:33.33333%}.col-md-6,.row.cols-md-6>*{max-width:50%;flex-basis:50%}.col-md-offset-5{margin-left:41.66667%}.col-md-7,.row.cols-md-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-md-offset-6{margin-left:50%}.col-md-8,.row.cols-md-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-md-offset-7{margin-left:58.33333%}.col-md-9,.row.cols-md-9>*{max-width:75%;flex-basis:75%}.col-md-offset-8{margin-left:66.66667%}.col-md-10,.row.cols-md-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-md-offset-9{margin-left:75%}.col-md-11,.row.cols-md-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-md-offset-10{margin-left:83.33333%}.col-md-12,.row.cols-md-12>*{max-width:100%;flex-basis:100%}.col-md-offset-11{margin-left:91.66667%}.col-md-normal{order:initial}.col-md-first{order:-999}.col-md-last{order:999}}@media screen and (min-width: 1280px){.col-lg,[class^='col-lg-'],[class^='col-lg-offset-'],.row[class*='cols-lg-']>*{box-sizing:border-box;flex:0 0 auto;padding:0 calc(var(--universal-padding) / 2)}.col-lg,.row.cols-lg>*{max-width:100%;flex-grow:1;flex-basis:0}.col-lg-1,.row.cols-lg-1>*{max-width:8.33333%;flex-basis:8.33333%}.col-lg-offset-0{margin-left:0}.col-lg-2,.row.cols-lg-2>*{max-width:16.66667%;flex-basis:16.66667%}.col-lg-offset-1{margin-left:8.33333%}.col-lg-3,.row.cols-lg-3>*{max-width:25%;flex-basis:25%}.col-lg-offset-2{margin-left:16.66667%}.col-lg-4,.row.cols-lg-4>*{max-width:33.33333%;flex-basis:33.33333%}.col-lg-offset-3{margin-left:25%}.col-lg-5,.row.cols-lg-5>*{max-width:41.66667%;flex-basis:41.66667%}.col-lg-offset-4{margin-left:33.33333%}.col-lg-6,.row.cols-lg-6>*{max-width:50%;flex-basis:50%}.col-lg-offset-5{margin-left:41.66667%}.col-lg-7,.row.cols-lg-7>*{max-width:58.33333%;flex-basis:58.33333%}.col-lg-offset-6{margin-left:50%}.col-lg-8,.row.cols-lg-8>*{max-width:66.66667%;flex-basis:66.66667%}.col-lg-offset-7{margin-left:58.33333%}.col-lg-9,.row.cols-lg-9>*{max-width:75%;flex-basis:75%}.col-lg-offset-8{margin-left:66.66667%}.col-lg-10,.row.cols-lg-10>*{max-width:83.33333%;flex-basis:83.33333%}.col-lg-offset-9{margin-left:75%}.col-lg-11,.row.cols-lg-11>*{max-width:91.66667%;flex-basis:91.66667%}.col-lg-offset-10{margin-left:83.33333%}.col-lg-12,.row.cols-lg-12>*{max-width:100%;flex-basis:100%}.col-lg-offset-11{margin-left:91.66667%}.col-lg-normal{order:initial}.col-lg-first{order:-999}.col-lg-last{order:999}}:root{--card-back-color:#f8f8f8;--card-fore-color:#111;--card-border-color:#ddd}.card{display:flex;flex-direction:column;justify-content:space-between;align-self:center;position:relative;width:100%;background:var(--card-back-color);color:var(--card-fore-color);border:.0625rem solid var(--card-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin);overflow:hidden}@media screen and (min-width: 320px){.card{max-width:320px}}.card>.section{background:var(--card-back-color);color:var(--card-fore-color);box-sizing:border-box;margin:0;border:0;border-radius:0;border-bottom:.0625rem solid var(--card-border-color);padding:var(--universal-padding);width:100%}.card>.section.media{height:200px;padding:0;-o-object-fit:cover;object-fit:cover}.card>.section:last-child{border-bottom:0}@media screen and (min-width: 240px){.card.small{max-width:240px}}@media screen and (min-width: 480px){.card.large{max-width:480px}}.card.fluid{max-width:100%;width:auto}.card.warning{--card-back-color:#ffca28;--card-border-color:#e8b825}.card.error{--card-back-color:#b71c1c;--card-fore-color:#f8f8f8;--card-border-color:#a71a1a}.card>.section.dark{--card-back-color:#e0e0e0}.card>.section.double-padded{padding:calc(1.5 * var(--universal-padding))}:root{--form-back-color:#f0f0f0;--form-fore-color:#111;--form-border-color:#ddd;--input-back-color:#f8f8f8;--input-fore-color:#111;--input-border-color:#ddd;--input-focus-color:#0288d1;--input-invalid-color:#d32f2f;--button-back-color:#e2e2e2;--button-hover-back-color:#dcdcdc;--button-fore-color:#212121;--button-border-color:transparent;--button-hover-border-color:transparent;--button-group-border-color:rgba(124,124,124,0.54)}form{background:var(--form-back-color);color:var(--form-fore-color);border:.0625rem solid var(--form-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin);padding:calc(2 * var(--universal-padding)) var(--universal-padding)}fieldset{border:.0625rem solid var(--form-border-color);border-radius:var(--universal-border-radius);margin:calc(var(--universal-margin) / 4);padding:var(--universal-padding)}legend{box-sizing:border-box;display:table;max-width:100%;white-space:normal;font-weight:700;padding:calc(var(--universal-padding) / 2)}label{padding:calc(var(--universal-padding) / 2) var(--universal-padding)}.input-group{display:inline-block}.input-group.fluid{display:flex;align-items:center;justify-content:center}.input-group.fluid>input{max-width:100%;flex-grow:1;flex-basis:0px}@media screen and (max-width: 767px){.input-group.fluid{align-items:stretch;flex-direction:column}}.input-group.vertical{display:flex;align-items:stretch;flex-direction:column}.input-group.vertical>input{max-width:100%;flex-grow:1;flex-basis:0px}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input:not([type]),[type="text"],[type="email"],[type="number"],[type="search"],[type="password"],[type="url"],[type="tel"],[type="checkbox"],[type="radio"],textarea,select{box-sizing:border-box;background:var(--input-back-color);color:var(--input-fore-color);border:.0625rem solid var(--input-border-color);border-radius:var(--universal-border-radius);margin:calc(var(--universal-margin) / 2);padding:var(--universal-padding) calc(1.5 * var(--universal-padding))}input:not([type="button"]):not([type="submit"]):not([type="reset"]):hover,input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus,textarea:hover,textarea:focus,select:hover,select:focus{border-color:var(--input-focus-color);box-shadow:none}input:not([type="button"]):not([type="submit"]):not([type="reset"]):invalid,input:not([type="button"]):not([type="submit"]):not([type="reset"]):focus:invalid,textarea:invalid,textarea:focus:invalid,select:invalid,select:focus:invalid{border-color:var(--input-invalid-color);box-shadow:none}input:not([type="button"]):not([type="submit"]):not([type="reset"])[readonly],textarea[readonly],select[readonly]{background:var(--secondary-back-color)}select{max-width:100%}option{overflow:hidden;text-overflow:ellipsis}[type="checkbox"],[type="radio"]{-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative;height:calc(1rem + var(--universal-padding) / 2);width:calc(1rem + var(--universal-padding) / 2);vertical-align:text-bottom;padding:0;flex-basis:calc(1rem + var(--universal-padding) / 2) !important;flex-grow:0 !important}[type="checkbox"]:checked:before,[type="radio"]:checked:before{position:absolute}[type="checkbox"]:checked:before{content:'\2713';font-family:sans-serif;font-size:calc(1rem + var(--universal-padding) / 2);top:calc(0rem - var(--universal-padding));left:calc(var(--universal-padding) / 4)}[type="radio"]{border-radius:100%}[type="radio"]:checked:before{border-radius:100%;content:'';top:calc(.0625rem + var(--universal-padding) / 2);left:calc(.0625rem + var(--universal-padding) / 2);background:var(--input-fore-color);width:0.5rem;height:0.5rem}:placeholder-shown{color:var(--input-fore-color)}::-ms-placeholder{color:var(--input-fore-color);opacity:0.54}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button{overflow:visible;text-transform:none}button,[type="button"],[type="submit"],[type="reset"],a.button,label.button,.button,a[role="button"],label[role="button"],[role="button"]{display:inline-block;background:var(--button-back-color);color:var(--button-fore-color);border:.0625rem solid var(--button-border-color);border-radius:var(--universal-border-radius);padding:var(--universal-padding) calc(1.5 * var(--universal-padding));margin:var(--universal-margin);text-decoration:none;cursor:pointer;transition:background 0.3s}button:hover,button:focus,[type="button"]:hover,[type="button"]:focus,[type="submit"]:hover,[type="submit"]:focus,[type="reset"]:hover,[type="reset"]:focus,a.button:hover,a.button:focus,label.button:hover,label.button:focus,.button:hover,.button:focus,a[role="button"]:hover,a[role="button"]:focus,label[role="button"]:hover,label[role="button"]:focus,[role="button"]:hover,[role="button"]:focus{background:var(--button-hover-back-color);border-color:var(--button-hover-border-color)}input:disabled,input[disabled],textarea:disabled,textarea[disabled],select:disabled,select[disabled],button:disabled,button[disabled],.button:disabled,.button[disabled],[role="button"]:disabled,[role="button"][disabled]{cursor:not-allowed;opacity:.75}.button-group{display:flex;border:.0625rem solid var(--button-group-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin)}.button-group>button,.button-group [type="button"],.button-group>[type="submit"],.button-group>[type="reset"],.button-group>.button,.button-group>[role="button"]{margin:0;max-width:100%;flex:1 1 auto;text-align:center;border:0;border-radius:0;box-shadow:none}.button-group>:not(:first-child){border-left:.0625rem solid var(--button-group-border-color)}@media screen and (max-width: 767px){.button-group{flex-direction:column}.button-group>:not(:first-child){border:0;border-top:.0625rem solid var(--button-group-border-color)}}button.primary,[type="button"].primary,[type="submit"].primary,[type="reset"].primary,.button.primary,[role="button"].primary{--button-back-color:#1976d2;--button-fore-color:#f8f8f8}button.primary:hover,button.primary:focus,[type="button"].primary:hover,[type="button"].primary:focus,[type="submit"].primary:hover,[type="submit"].primary:focus,[type="reset"].primary:hover,[type="reset"].primary:focus,.button.primary:hover,.button.primary:focus,[role="button"].primary:hover,[role="button"].primary:focus{--button-hover-back-color:#1565c0}button.secondary,[type="button"].secondary,[type="submit"].secondary,[type="reset"].secondary,.button.secondary,[role="button"].secondary{--button-back-color:#d32f2f;--button-fore-color:#f8f8f8}button.secondary:hover,button.secondary:focus,[type="button"].secondary:hover,[type="button"].secondary:focus,[type="submit"].secondary:hover,[type="submit"].secondary:focus,[type="reset"].secondary:hover,[type="reset"].secondary:focus,.button.secondary:hover,.button.secondary:focus,[role="button"].secondary:hover,[role="button"].secondary:focus{--button-hover-back-color:#c62828}button.tertiary,[type="button"].tertiary,[type="submit"].tertiary,[type="reset"].tertiary,.button.tertiary,[role="button"].tertiary{--button-back-color:#308732;--button-fore-color:#f8f8f8}button.tertiary:hover,button.tertiary:focus,[type="button"].tertiary:hover,[type="button"].tertiary:focus,[type="submit"].tertiary:hover,[type="submit"].tertiary:focus,[type="reset"].tertiary:hover,[type="reset"].tertiary:focus,.button.tertiary:hover,.button.tertiary:focus,[role="button"].tertiary:hover,[role="button"].tertiary:focus{--button-hover-back-color:#277529}button.inverse,[type="button"].inverse,[type="submit"].inverse,[type="reset"].inverse,.button.inverse,[role="button"].inverse{--button-back-color:#212121;--button-fore-color:#f8f8f8}button.inverse:hover,button.inverse:focus,[type="button"].inverse:hover,[type="button"].inverse:focus,[type="submit"].inverse:hover,[type="submit"].inverse:focus,[type="reset"].inverse:hover,[type="reset"].inverse:focus,.button.inverse:hover,.button.inverse:focus,[role="button"].inverse:hover,[role="button"].inverse:focus{--button-hover-back-color:#111}button.small,[type="button"].small,[type="submit"].small,[type="reset"].small,.button.small,[role="button"].small{padding:calc(0.5 * var(--universal-padding)) calc(0.75 * var(--universal-padding));margin:var(--universal-margin)}button.large,[type="button"].large,[type="submit"].large,[type="reset"].large,.button.large,[role="button"].large{padding:calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding));margin:var(--universal-margin)}:root{--header-back-color:#f8f8f8;--header-hover-back-color:#f0f0f0;--header-fore-color:#444;--header-border-color:#ddd;--nav-back-color:#f8f8f8;--nav-hover-back-color:#f0f0f0;--nav-fore-color:#444;--nav-border-color:#ddd;--nav-link-color:#0277bd;--footer-fore-color:#444;--footer-back-color:#f8f8f8;--footer-border-color:#ddd;--footer-link-color:#0277bd;--drawer-back-color:#f8f8f8;--drawer-hover-back-color:#f0f0f0;--drawer-border-color:#ddd;--drawer-close-color:#444}header{height:3.1875rem;background:var(--header-back-color);color:var(--header-fore-color);border-bottom:.0625rem solid var(--header-border-color);padding:calc(var(--universal-padding) / 4) 0;white-space:nowrap;overflow-x:auto;overflow-y:hidden}header.row{box-sizing:content-box}header .logo{color:var(--header-fore-color);font-size:1.75rem;padding:var(--universal-padding) calc(2 * var(--universal-padding));text-decoration:none}header button,header [type="button"],header .button,header [role="button"]{box-sizing:border-box;position:relative;top:calc(0rem - var(--universal-padding) / 4);height:calc(3.1875rem + var(--universal-padding) / 2);background:var(--header-back-color);line-height:calc(3.1875rem - var(--universal-padding) * 1.5);text-align:center;color:var(--header-fore-color);border:0;border-radius:0;margin:0;text-transform:uppercase}header button:hover,header button:focus,header [type="button"]:hover,header [type="button"]:focus,header .button:hover,header .button:focus,header [role="button"]:hover,header [role="button"]:focus{background:var(--header-hover-back-color)}nav{background:var(--nav-back-color);color:var(--nav-fore-color);border:.0625rem solid var(--nav-border-color);border-radius:var(--universal-border-radius);margin:var(--universal-margin)}nav *{padding:var(--universal-padding) calc(1.5 * var(--universal-padding))}nav a,nav a:visited{display:block;color:var(--nav-link-color);border-radius:var(--universal-border-radius);transition:background 0.3s}nav a:hover,nav a:focus,nav a:visited:hover,nav a:visited:focus{text-decoration:none;background:var(--nav-hover-back-color)}nav .sublink-1{position:relative;margin-left:calc(2 * var(--universal-padding))}nav .sublink-1:before{position:absolute;left:calc(var(--universal-padding) - 1 * var(--universal-padding));top:-.0625rem;content:'';height:100%;border:.0625rem solid var(--nav-border-color);border-left:0}nav .sublink-2{position:relative;margin-left:calc(4 * var(--universal-padding))}nav .sublink-2:before{position:absolute;left:calc(var(--universal-padding) - 3 * var(--universal-padding));top:-.0625rem;content:'';height:100%;border:.0625rem solid var(--nav-border-color);border-left:0}footer{background:var(--footer-back-color);color:var(--footer-fore-color);border-top:.0625rem solid var(--footer-border-color);padding:calc(2 * var(--universal-padding)) var(--universal-padding);font-size:.875rem}footer a,footer a:visited{color:var(--footer-link-color)}header.sticky{position:-webkit-sticky;position:sticky;z-index:1101;top:0}footer.sticky{position:-webkit-sticky;position:sticky;z-index:1101;bottom:0}.drawer-toggle:before{display:inline-block;position:relative;vertical-align:bottom;content:'\00a0\2261\00a0';font-family:sans-serif;font-size:1.5em}@media screen and (min-width: 768px){.drawer-toggle:not(.persistent){display:none}}[type="checkbox"].drawer{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}[type="checkbox"].drawer+*{display:block;box-sizing:border-box;position:fixed;top:0;width:320px;height:100vh;overflow-y:auto;background:var(--drawer-back-color);border:.0625rem solid var(--drawer-border-color);border-radius:0;margin:0;z-index:1110;left:-320px;transition:left 0.3s}[type="checkbox"].drawer+* .drawer-close{position:absolute;top:var(--universal-margin);right:var(--universal-margin);z-index:1111;width:2rem;height:2rem;border-radius:var(--universal-border-radius);padding:var(--universal-padding);margin:0;cursor:pointer;transition:background 0.3s}[type="checkbox"].drawer+* .drawer-close:before{display:block;content:'\00D7';color:var(--drawer-close-color);position:relative;font-family:sans-serif;font-size:2rem;line-height:1;text-align:center}[type="checkbox"].drawer+* .drawer-close:hover,[type="checkbox"].drawer+* .drawer-close:focus{background:var(--drawer-hover-back-color)}@media screen and (max-width: 320px){[type="checkbox"].drawer+*{width:100%}}[type="checkbox"].drawer:checked+*{left:0}@media screen and (min-width: 768px){[type="checkbox"].drawer:not(.persistent)+*{position:static;height:100%;z-index:1100}[type="checkbox"].drawer:not(.persistent)+* .drawer-close{display:none}}:root{--mark-back-color:#0277bd;--mark-fore-color:#fafafa}mark{background:var(--mark-back-color);color:var(--mark-fore-color);font-size:.95em;line-height:1em;border-radius:var(--universal-border-radius);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2)}mark.inline-block{display:inline-block;font-size:1em;line-height:1.5;padding:calc(var(--universal-padding) / 2) var(--universal-padding)}:root{--toast-back-color:#424242;--toast-fore-color:#fafafa}.toast{position:fixed;bottom:calc(var(--universal-margin) * 3);left:50%;transform:translate(-50%, -50%);z-index:1111;color:var(--toast-fore-color);background:var(--toast-back-color);border-radius:calc(var(--universal-border-radius) * 16);padding:var(--universal-padding) calc(var(--universal-padding) * 3)}:root{--tooltip-back-color:#212121;--tooltip-fore-color:#fafafa}.tooltip{position:relative;display:inline-block}.tooltip:before,.tooltip:after{position:absolute;opacity:0;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%);transition:all 0.3s;z-index:1010;left:50%}.tooltip:not(.bottom):before,.tooltip:not(.bottom):after{bottom:75%}.tooltip.bottom:before,.tooltip.bottom:after{top:75%}.tooltip:hover:before,.tooltip:hover:after,.tooltip:focus:before,.tooltip:focus:after{opacity:1;clip:auto;-webkit-clip-path:inset(0%);clip-path:inset(0%)}.tooltip:before{content:'';background:transparent;border:var(--universal-margin) solid transparent;left:calc(50% - var(--universal-margin))}.tooltip:not(.bottom):before{border-top-color:#212121}.tooltip.bottom:before{border-bottom-color:#212121}.tooltip:after{content:attr(aria-label);color:var(--tooltip-fore-color);background:var(--tooltip-back-color);border-radius:var(--universal-border-radius);padding:var(--universal-padding);white-space:nowrap;transform:translateX(-50%)}.tooltip:not(.bottom):after{margin-bottom:calc(2 * var(--universal-margin))}.tooltip.bottom:after{margin-top:calc(2 * var(--universal-margin))}:root{--modal-overlay-color:rgba(0,0,0,0.45);--modal-close-color:#444;--modal-close-hover-color:#f0f0f0}[type="checkbox"].modal{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}[type="checkbox"].modal+div{position:fixed;top:0;left:0;display:none;width:100vw;height:100vh;background:var(--modal-overlay-color)}[type="checkbox"].modal+div .card{margin:0 auto;max-height:50vh;overflow:auto}[type="checkbox"].modal+div .card .modal-close{position:absolute;top:0;right:0;width:1.75rem;height:1.75rem;border-radius:var(--universal-border-radius);padding:var(--universal-padding);margin:0;cursor:pointer;transition:background 0.3s}[type="checkbox"].modal+div .card .modal-close:before{display:block;content:'\00D7';color:var(--modal-close-color);position:relative;font-family:sans-serif;font-size:1.75rem;line-height:1;text-align:center}[type="checkbox"].modal+div .card .modal-close:hover,[type="checkbox"].modal+div .card .modal-close:focus{background:var(--modal-close-hover-color)}[type="checkbox"].modal:checked+div{display:flex;flex:0 1 auto;z-index:1200}[type="checkbox"].modal:checked+div .card .modal-close{z-index:1211}:root{--collapse-label-back-color:#e8e8e8;--collapse-label-fore-color:#212121;--collapse-label-hover-back-color:#f0f0f0;--collapse-selected-label-back-color:#ececec;--collapse-border-color:#ddd;--collapse-content-back-color:#fafafa;--collapse-selected-label-border-color:#0277bd}.collapse{width:calc(100% - 2 * var(--universal-margin));opacity:1;display:flex;flex-direction:column;margin:var(--universal-margin);border-radius:var(--universal-border-radius)}.collapse>[type="radio"],.collapse>[type="checkbox"]{height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%)}.collapse>label{flex-grow:1;display:inline-block;height:1.5rem;cursor:pointer;transition:background 0.3s;color:var(--collapse-label-fore-color);background:var(--collapse-label-back-color);border:.0625rem solid var(--collapse-border-color);padding:calc(1.5 * var(--universal-padding))}.collapse>label:hover,.collapse>label:focus{background:var(--collapse-label-hover-back-color)}.collapse>label+div{flex-basis:auto;height:1px;width:1px;margin:-1px;overflow:hidden;position:absolute;clip:rect(0 0 0 0);-webkit-clip-path:inset(100%);clip-path:inset(100%);transition:max-height 0.3s;max-height:1px}.collapse>:checked+label{background:var(--collapse-selected-label-back-color);border-bottom-color:var(--collapse-selected-label-border-color)}.collapse>:checked+label+div{box-sizing:border-box;position:relative;width:100%;height:auto;overflow:auto;margin:0;background:var(--collapse-content-back-color);border:.0625rem solid var(--collapse-border-color);border-top:0;padding:var(--universal-padding);clip:auto;-webkit-clip-path:inset(0%);clip-path:inset(0%);max-height:400px}.collapse>label:not(:first-of-type){border-top:0}.collapse>label:first-of-type{border-radius:var(--universal-border-radius) var(--universal-border-radius) 0 0}.collapse>label:last-of-type{border-radius:0 0 var(--universal-border-radius) var(--universal-border-radius)}.collapse>:checked:last-of-type+label{border-radius:0}.collapse>:checked:last-of-type+label+div{border-radius:0 0 var(--universal-border-radius) var(--universal-border-radius)}mark.secondary{--mark-back-color:#d32f2f}mark.tertiary{--mark-back-color:#308732}mark.tag{padding:calc(var(--universal-padding)/2) var(--universal-padding);border-radius:1em}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}code{transform:scale(1)}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;text-align:center}header h1.logo a{text-decoration:none;color:#111}header #title{position:relative;top:-1rem}@media screen and (max-width: 500px){header #title{font-size:1rem;display:block}}header h1 small{display:block;font-size:0.875rem;font-style:italic;color:#888;margin-top:-0.8rem}@media screen and (max-width: 768px){header h1 small{font-size:0.75rem}}@media screen and (max-width: 600px){header h1 small{font-size:0.625rem}}@media screen and (max-width: 500px){header h1 small{font-size:0.5rem;margin-top:-1.2rem}}label#menu-toggle{position:absolute;left:0.5rem;top:0.5rem;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}div.collapse{margin:0;width:100%}div.collapse>label{border-top-left-radius:var(--universal-border-radius) !important;border-top-right-radius:var(--universal-border-radius) !important}div.collapse>div{padding:0 !important}div.collapse>div>pre{margin:0;border:0}button.primary.clipboard-copy{width:100%;margin-left:0}button.primary.clipboard-copy>svg{vertical-align:bottom}
diff --git a/docs/mini/flavor.scss b/docs/mini/flavor.scss
index 13f369a30..e1a957cb1 100644
--- a/docs/mini/flavor.scss
+++ b/docs/mini/flavor.scss
@@ -121,7 +121,7 @@ code {
   transform: scale(1);  /* Deals with the issue described in #243 */
 }
 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}
+.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}
diff --git a/snippets/capitalize.md b/snippets/capitalize.md
index 802bd7a4f..f9a26e7be 100644
--- a/snippets/capitalize.md
+++ b/snippets/capitalize.md
@@ -1,4 +1,4 @@
-### Capitalize
+### capitalize
 
 Capitalizes the first letter of a string.
 
diff --git a/snippets/elo.md b/snippets/elo.md
new file mode 100644
index 000000000..671d1ccea
--- /dev/null
+++ b/snippets/elo.md
@@ -0,0 +1,24 @@
+### elo
+
+Computes the new ratings between two opponents using the [Elo rating system](https://en.wikipedia.org/wiki/Elo_rating_system). It takes an array
+of two pre-ratings and returns an array containing two post-ratings.
+The winner's rating is the first element of the array.
+
+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. Omit the second argument to use the default
+K-factor of 32, or supply a custom K-factor value.
+
+```js
+const elo = ([a, b], kFactor = 32) => {
+  const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
+  const newRating = (rating, i) => rating + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
+  return [newRating(a, 1), newRating(b, 0)];
+};
+```
+
+```js
+elo([1200, 1200]); // [1216, 1184]
+elo([1000, 2000]); // [1031.8991261061358, 1968.1008738938642]
+elo([1500, 1000]); // [1501.7036868864648, 998.2963131135352]
+elo([1200, 1200], 64); // [1232, 1168]
+```
diff --git a/snippets/isAbsoluteURL.md b/snippets/isAbsoluteURL.md
new file mode 100644
index 000000000..00d8d8936
--- /dev/null
+++ b/snippets/isAbsoluteURL.md
@@ -0,0 +1,15 @@
+### isAbsoluteURL
+
+Returns `true` if the given string is an absolute URL, `false` otherwise.
+
+Use a regular expression to test if the string is an absolute URL.
+
+```js
+const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
+```
+
+```js
+isAbsoluteURL('https://google.com'); // true
+isAbsoluteURL('ftp://www.myserver.net'); // true
+isAbsoluteURL('/foo/bar'); // false
+```
diff --git a/snippets/isArmstrongNumber.md b/snippets/isArmstrongNumber.md
index af6ba739b..8f8ae8ed3 100644
--- a/snippets/isArmstrongNumber.md
+++ b/snippets/isArmstrongNumber.md
@@ -2,11 +2,11 @@
 
 Checks if the given number is an Armstrong number or not.
 
-Convert the given number into an array of digits. Use `Math.pow()` to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return `true` otherwise `false`.
+Convert the given number into an array of digits. Use the exponent operator (`**`) to get the appropriate power for each digit and sum them up. If the sum is equal to the number itself, return `true` otherwise `false`.
 
 ```js
 const isArmstrongNumber = digits =>
-  (arr => arr.reduce((a, d) => a + Math.pow(parseInt(d), arr.length), 0) == digits)(
+  (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)(
     (digits + '').split('')
   );
 ```
diff --git a/snippets/isArrayLike.md b/snippets/isArrayLike.md
index 83c8acb81..eff62aae0 100644
--- a/snippets/isArrayLike.md
+++ b/snippets/isArrayLike.md
@@ -2,22 +2,19 @@
 
 Checks if the provided argument is array-like (i.e. is iterable).
 
-Use `Array.from()` and a `try... catch` block to check if the provided argument is array-like.
+Use the spread operator (`...`) to check if the provided argument is iterable inside a `try... catch` block and the comma operator (`,`) to return the appropriate value.
 
 ```js
-const isArrayLike = arr => {
-  try{
-    Array.from(arr);
-    return true;
-  }
-  catch(e){
-    return false;
-  }
-}
+
+
+const isArrayLike = val =>
+  try {return [...val], true; }
+  catch (e)  { return false; }
+};
 ```
 
 ```js
-isArrayLike(document.querySelector('.className')) // true
-isArrayLike('abc') // true
-isArrayLike(null) // false
+isArrayLike(document.querySelectorAll('.className')); // true
+isArrayLike('abc'); // true
+isArrayLike(null); // false
 ```
diff --git a/snippets/isPrimitive.md b/snippets/isPrimitive.md
new file mode 100644
index 000000000..3abe155c4
--- /dev/null
+++ b/snippets/isPrimitive.md
@@ -0,0 +1,22 @@
+### isPrimitive
+
+Returns a boolean determining if the supplied value is primitive or not.
+
+Use `Array.includes()` on an array of type strings which are not primitive,
+supplying the type using `typeof`.
+Since `typeof null` evaluates to `'object'`, it needs to be directly compared.
+
+```js
+const isPrimitive = val => !['object', 'function'].includes(typeof val) || val === null;
+```
+
+```js
+isPrimitive(window.someNonExistentProperty); // true
+isPrimitive(null); // true
+isPrimitive(50); // true
+isPrimitive('Hello!'); // true
+isPrimitive(false); // true
+isPrimitive(Symbol()); // true
+isPrimitive([]); // false
+isPrimitive(new String('Hello!')); // false
+```
diff --git a/snippets/isPromiseLike.md b/snippets/isPromiseLike.md
new file mode 100644
index 000000000..6a2ae29d4
--- /dev/null
+++ b/snippets/isPromiseLike.md
@@ -0,0 +1,22 @@
+### isPromiseLike
+
+Returns `true` if an object looks like a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), `false` otherwise.
+
+Check if the object is not `null`, its `typeof` matches either `object` or `function` and if it has a `.then` property, which is also a `function`.
+
+```js
+const isPromiseLike = obj =>
+  obj !== null &&
+  (typeof obj === 'object' || typeof obj === 'function') &&
+  typeof obj.then === 'function';
+```
+
+```js
+isPromiseLike({
+  then: function() {
+    return '';
+  }
+}); // true
+isPromiseLike(null); // false
+isPromiseLike({}); // false
+```
diff --git a/snippets/isValidJSON.md b/snippets/isValidJSON.md
index 56211961b..51b9270a2 100644
--- a/snippets/isValidJSON.md
+++ b/snippets/isValidJSON.md
@@ -6,17 +6,17 @@ Use `JSON.parse()` and a `try... catch` block to check if the provided argument
 
 ```js
 const isValidJSON = obj => {
-  try{
+  try {
     JSON.parse(obj);
     return true;
-  }
-  catch(e){
+  } catch (e) {
     return false;
   }
-}
+};
 ```
 
 ```js
 isValidJSON('{"name":"Adam","age":20}'); // true
 isValidJSON('{"name":"Adam",age:"20"}'); // false
+isValidJSON(null); // true
 ```
diff --git a/snippets/join.md b/snippets/join.md
index 476af7b15..bcc63edbf 100644
--- a/snippets/join.md
+++ b/snippets/join.md
@@ -15,12 +15,11 @@ const join = (arr, separator = ',', end = separator) =>
         : i == arr.length - 1 ? acc + val : acc + val + separator,
     ''
   );
-
 ```
 
 ```js
 join(); // ''
-join(['pen','pineapple','apple','pen'],',','&'); //"pen,pineapple,apple&pen"
-join(['pen','pineapple','apple','pen'],','); //"pen,pineapple,apple,pen"
-join(['pen','pineapple','apple','pen']); //"pen,pineapple,apple,pen"
+join(['pen', 'pineapple', 'apple', 'pen'], ',', '&'); //"pen,pineapple,apple&pen"
+join(['pen', 'pineapple', 'apple', 'pen'], ','); //"pen,pineapple,apple,pen"
+join(['pen', 'pineapple', 'apple', 'pen']); //"pen,pineapple,apple,pen"
 ```
diff --git a/snippets/mask.md b/snippets/mask.md
index e69de29bb..a3195ab46 100644
--- a/snippets/mask.md
+++ b/snippets/mask.md
@@ -0,0 +1,20 @@
+### mask
+
+Replaces all but the last `num` of characters with the specified mask character.
+
+Use `String.slice()` to grab the portion of the characters that need to be masked and use `String.replace()` with a regex to replace every character with the mask character. 
+Concatenate the masked characters with the remaining unmasked portion of the string.
+Omit the second argument, `num`, to keep a default of `4` characters unmasked. If `num` is negative, the unmasked characters will be at the start of the string.
+Omit the third argument, `mask`, to use a default character of `'*'` for the mask.
+
+```js
+const mask = (cc, num = 4, mask = '*') =>
+  ('' + cc).slice(0, -num).replace(/./g, mask) + ('' + cc).slice(-num);
+```
+
+```js
+mask(1234567890); // '******7890'
+mask(1234567890, 3); // '*******890'
+mask(1234567890, 4, '$'); // '$$$$$$7890'
+mask(1234567890, -4, '$'); // '1234$$$$$$'
+```
diff --git a/snippets/memoize.md b/snippets/memoize.md
new file mode 100644
index 000000000..c1b6b1343
--- /dev/null
+++ b/snippets/memoize.md
@@ -0,0 +1,20 @@
+### memoize
+
+Returns the memoized (cached) function.
+
+Use `Object.create(null)` to create an empty object without `Object.prototype` (so that those properties are not resolved if the input value is something like `'hasOwnProperty'`).
+Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not.
+
+```js
+const memoize = fn => {
+  const cache = Object.create(null);
+  return value => cache[value] || (cache[value] = fn(value));
+};
+```
+
+```js
+// See the `anagrams` snippet.
+const anagramsCached = memoize(anagrams);
+anagramsCached('javascript'); // takes a long time
+anagramsCached('javascript'); // returns virtually instantly since it's now cached
+```
diff --git a/snippets/sampleSize.md b/snippets/sampleSize.md
index f6187bdea..7b2480cba 100644
--- a/snippets/sampleSize.md
+++ b/snippets/sampleSize.md
@@ -7,17 +7,17 @@ Use `Array.slice()` to get the first `n` elements.
 Omit the second argument, `n` to get only one element at random from the array.
 
 ```js
-const sampleSize = ([...arr],n=1) => {
+const sampleSize = ([...arr], n = 1) => {
   let m = arr.length;
   while (m) {
     const i = Math.floor(Math.random() * m--);
     [arr[m], arr[i]] = [arr[i], arr[m]];
   }
-  return arr.slice(0,n)
-}
+  return arr.slice(0, n);
+};
 ```
 
 ```js
-sampleSize([1,2,3],2); // [3,1]
-sampleSize([1,2,3],4); // [2,3,1]
+sampleSize([1, 2, 3], 2); // [3,1]
+sampleSize([1, 2, 3], 4); // [2,3,1]
 ```
diff --git a/snippets/sortedIndex.md b/snippets/sortedIndex.md
index f83da6ec0..e8d6a6d86 100644
--- a/snippets/sortedIndex.md
+++ b/snippets/sortedIndex.md
@@ -8,12 +8,12 @@ Use `Array.findIndex()` to find the appropriate index where the element should b
 ```js
 const sortedIndex = (arr, n) => {
   const isDescending = arr[0] > arr[arr.length - 1];
-  const index = arr.findIndex(el => isDescending ? n >= el : n <= el);
+  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
   return index === -1 ? arr.length : index;
 };
 ```
 
 ```js
-sortedIndex([5,3,2,1], 4); // 1
-sortedIndex([30,50],40); // 1
+sortedIndex([5, 3, 2, 1], 4); // 1
+sortedIndex([30, 50], 40); // 1
 ```
diff --git a/snippets/standardDeviation.md b/snippets/standardDeviation.md
index fe26f58f2..09d1d7981 100644
--- a/snippets/standardDeviation.md
+++ b/snippets/standardDeviation.md
@@ -10,9 +10,7 @@ You can omit the second argument to get the sample standard deviation or set it
 const standardDeviation = (arr, usePopulation = false) => {
   const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
   return Math.sqrt(
-    arr
-      .reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), [])
-      .reduce((acc, val) => acc + val, 0) /
+    arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) /
       (arr.length - (usePopulation ? 0 : 1))
   );
 };
diff --git a/static-parts/README-end.md b/static-parts/README-end.md
index c9d2d14b1..8b9c13e84 100644
--- a/static-parts/README-end.md
+++ b/static-parts/README-end.md
@@ -1,3 +1,10 @@
+## Collaborators
+
+| [](https://github.com/Chalarangelo)
[Angelos Chalaris](https://github.com/Chalarangelo) | [](https://github.com/Pl4gue)
[David Wu](https://github.com/Pl4gue) | [](https://github.com/fejes713)
[Stefan Feješ](https://github.com/fejes713) | [](https://github.com/kingdavidmartins)
[King David Martins](https://github.com/iamsoorena) | [](https://github.com/iamsoorena)
[Soorena Soleimani](https://github.com/iamsoorena) | +| --- | --- | --- | --- | --- | +| [](https://github.com/elderhsouza)
[Elder Henrique Souza](https://github.com/elderhsouza) | [](https://github.com/skatcat31)
[Robert Mennell](https://github.com/skatcat31) | [](https://github.com/atomiks)
[atomiks](https://github.com/atomiks) | + + ## Credits *Icons made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/).* diff --git a/static-parts/index-start.html b/static-parts/index-start.html index 2dd7839c9..0dfd76c9d 100644 --- a/static-parts/index-start.html +++ b/static-parts/index-start.html @@ -92,8 +92,6 @@