├── .eslintrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist ├── addClass.js ├── afterFirst.js ├── afterLast.js ├── beforeFirst.js ├── beforeLast.js ├── byAlphabeticalOrder.js ├── byProps.js ├── deepGroupBy.js ├── eventAsPromise.js ├── formatBemClass.js ├── getAttr.js ├── getClippingInfo.js ├── getDistanceBetweenCoords.js ├── getEventPath.js ├── hasClass.js ├── ignore.js ├── internal │ ├── bem │ │ ├── _formatBemClassFromRadical.js │ │ ├── _formatBemModifier.js │ │ ├── _formatBemRadical.js │ │ ├── _hasAllModifiersSetToFalse.js │ │ ├── _hasNoModifiers.js │ │ ├── _isValidBemObject.js │ │ ├── _isValidDelimiterArray.js │ │ ├── _isValidModifierObject.js │ │ ├── _joinBemEntityWithDelimiter.js │ │ ├── _parseBem.js │ │ ├── _parseBemEntityWithFunc.js │ │ ├── _parseBemProp.js │ │ ├── _parseModifier.js │ │ ├── _parseModifierProp.js │ │ └── _validateModifyBemClassArgs.js │ ├── classname │ │ ├── _changeClassWithMethod.js │ │ ├── _removeClassesBeginningWithButNot.js │ │ └── _removeClassesBeginningWithButNotBase.js │ ├── dom │ │ ├── _createDomElement.js │ │ ├── _createFragment.js │ │ ├── _domElementsOrDocumentOrWindowToArray.js │ │ ├── _domElementsToArray.js │ │ ├── _domToArray.js │ │ ├── _isBooleanAttribute.js │ │ ├── _isElementOrDocumentOrWindow.js │ │ └── _setAttrBase.js │ ├── event │ │ ├── _reactToEvent.js │ │ └── _splitEventStr.js │ ├── geometry │ │ ├── _getAxisEndProp.js │ │ ├── _getAxisInfo.js │ │ ├── _getAxisStartProp.js │ │ ├── _getCoords.js │ │ ├── _getCoordsFromElement.js │ │ ├── _getCoordsFromElementOrObjectOrWindow.js │ │ ├── _getHorizontalAxisInfo.js │ │ └── _getVerticalAxisInfo.js │ ├── helpers │ │ ├── _isBoolean.js │ │ ├── _isElement.js │ │ ├── _isPlainObject.js │ │ ├── _isString.js │ │ ├── _parsePath.js │ │ ├── _random.js │ │ ├── _simpleAt.js │ │ └── _simpleSet.js │ ├── sort │ │ ├── _customComparator.js │ │ ├── _defaultComparator.js │ │ ├── _parseSortField.js │ │ ├── _parseSortFields.js │ │ └── _recursiveSort.js │ ├── string │ │ └── _sliceOnOccurrence.js │ └── validation │ │ └── _throwErrorIf.js ├── listen.js ├── modifyBemClass.js ├── modifyBemClassCompact.js ├── oneOutOf.js ├── parents.js ├── removeAttr.js ├── removeAttrs.js ├── removeClass.js ├── selfAndParents.js ├── setAttr.js ├── setAttrs.js ├── timeSince.js ├── toAverage.js ├── toAverageProp.js ├── toClosest.js ├── toClosestProp.js ├── toLargestProp.js ├── toSmallestProp.js ├── toSum.js ├── trigger.js └── waitInPromise.js ├── docs ├── CNAME ├── Gemfile ├── Gemfile.lock ├── _config.yml ├── _ejs │ ├── content.ejs │ ├── contentItem.ejs │ ├── contentLoop.ejs │ ├── itemExamples.ejs │ ├── itemParameters.ejs │ ├── itemReturns.ejs │ ├── itemTypeDefParams.ejs │ ├── itemTypeDefReturns.ejs │ ├── menu.ejs │ └── menuLoop.ejs ├── _includes │ ├── about.html │ ├── about.md │ ├── canivete-with-rollup.html │ ├── canivete-with-rollup.md │ ├── canivete-with-webpack.html │ ├── canivete-with-webpack.md │ ├── credits.html │ ├── credits.md │ ├── footer.html │ ├── head.html │ ├── header.html │ ├── jquery.scrollTo.min.js │ ├── kerning.min.js │ ├── logo.html │ ├── logo.md │ ├── menu.html │ ├── menu.md │ ├── pagenav.html │ ├── scrollingelement.js │ ├── usage.html │ └── usage.md ├── _layouts │ └── default.html ├── _sass │ ├── _content.scss │ ├── _grid.scss │ ├── _html.scss │ ├── _monokai.scss │ ├── _nav.scss │ ├── _page.scss │ ├── _scrollbar.scss │ ├── _typography.scss │ └── _variables.scss ├── _script │ └── run.js ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── canivete.jpg ├── canivete.png ├── css │ ├── kern.css │ └── styles.scss ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── fonts │ ├── 685260 │ │ ├── 0303195D8C6D5AA82.css │ │ ├── 035AEABEDE40158BE.eot │ │ ├── 073C176AF68C71AB5.eot │ │ ├── 0B5ECE65B2B4D42E3.css │ │ ├── 1155D4E37D66DB051.eot │ │ ├── 1A07CBFFDF24B5F29.css │ │ ├── 234A5715AC92A0316.css │ │ ├── 2F78A6109234676AC.css │ │ ├── 4041214EAB7F284B4.css │ │ ├── 40D4B15013983F3B9.eot │ │ ├── 449A5CFFBB492D3C1.eot │ │ ├── 4C5D90B170D6E7C2A.eot │ │ ├── 4E3A7AF39E66D4CC1.css │ │ ├── 518017E1A58563153.css │ │ ├── 56902F31DD424D730.css │ │ ├── 56E5F54B429716EA5.css │ │ ├── 5A11102025C97F841.css │ │ ├── 5D7E8B5F1B68CE0CD.css │ │ ├── 651E84E2C40547295.css │ │ ├── 67A2BFBBC92233B04.css │ │ ├── 6B61CCC6C24A4EB74.css │ │ ├── 6BE8C6AFC5BFAB166.eot │ │ ├── 6CC658FA047B9A5E4.css │ │ ├── 71ED6E15CBE60A2C2.eot │ │ ├── 78956558E73205ADB.css │ │ ├── 79334043303ED724B.css │ │ ├── 7A263DC97C039891D.css │ │ ├── 7AC09334CF6D5E1BA.css │ │ ├── 7F8B387F8F2396EB0.eot │ │ ├── 83FB9B1DA953E306B.eot │ │ ├── 868750EF172AD446E.css │ │ ├── 86D7EA966FA0AD1F9.css │ │ ├── 87E1A00C92D7DDD7E.css │ │ ├── 88291CD779FCAE673.css │ │ ├── 89521A4AC6B3DD4E2.css │ │ ├── 98F3678B3231FBD36.css │ │ ├── A3123A04FCA75E8A4.css │ │ ├── A476E19F63A389192.eot │ │ ├── A9F5E30A4E77A6594.css │ │ ├── AE4FC5648EFBC108A.eot │ │ ├── B3878FB1F112B1639.css │ │ ├── B3B279C3071BB4EBE.css │ │ ├── B633E65E15A793E2B.eot │ │ ├── C10E74D343407F323.css │ │ ├── CAD96F81BEB2F46D4.css │ │ ├── D170738708CDC33A3.css │ │ ├── D2374AF51704B368E.css │ │ ├── DA9BF74E3D3526BDB.css │ │ ├── E4CA561FADD393616.css │ │ ├── E70BA8416428E1CDA.eot │ │ ├── EC82E6DEFE07F7433.css │ │ ├── F13371865BB5CAE0F.css │ │ ├── F752CE3279BA25FC2.css │ │ └── F8528AE5F706FC0D1.css │ ├── eot │ │ ├── FiraCode-Bold.eot │ │ ├── FiraCode-Light.eot │ │ ├── FiraCode-Medium.eot │ │ └── FiraCode-Regular.eot │ ├── fira_code.css │ ├── otf │ │ ├── FiraCode-Bold.otf │ │ ├── FiraCode-Light.otf │ │ ├── FiraCode-Medium.otf │ │ ├── FiraCode-Regular.otf │ │ └── FiraCode-Retina.otf │ ├── specimen.html │ ├── ttf │ │ ├── FiraCode-Bold.ttf │ │ ├── FiraCode-Light.ttf │ │ ├── FiraCode-Medium.ttf │ │ ├── FiraCode-Regular.ttf │ │ └── FiraCode-Retina.ttf │ ├── woff │ │ ├── FiraCode-Bold.woff │ │ ├── FiraCode-Light.woff │ │ ├── FiraCode-Medium.woff │ │ └── FiraCode-Regular.woff │ └── woff2 │ │ ├── FiraCode-Bold.woff2 │ │ ├── FiraCode-Light.woff2 │ │ ├── FiraCode-Medium.woff2 │ │ └── FiraCode-Regular.woff2 ├── index.js ├── index.md ├── js │ └── index.js ├── manifest.json ├── media │ ├── canivete.svg │ └── github.svg ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png └── safari-pinned-tab.svg ├── karma.conf.js ├── package-lock.json ├── package.json ├── rollup.docs.config.js ├── rollup.test.config.js └── test ├── _domElementsAsArraySpecs.js ├── _getCoordsSpecs.js ├── _isBooleanSpecs.js ├── _isPlainObjectSpecs.js ├── _isStringSpecs.js ├── _isValidBemObjectSpecs.js ├── _isValidDelimiterArraySpecs.js ├── _isValidModifierObjectSpecs.js ├── _removeClassesBeginningWithButNotSpecs.js ├── addClassSpecs.js ├── afterFirstSpecs.js ├── afterLastSpecs.js ├── beforeFirstSpecs.js ├── beforeLastSpecs.js ├── byAlphabeticalOrderSpecs.js ├── byPropsSpecs.js ├── deepGroupBySpecs.js ├── eventAsPromiseSpecs.js ├── formatBemClassSpecs.js ├── getAttrSpecs.js ├── getClippingInfoSpecs.js ├── getDistanceBetweenCoordsSpecs.js ├── getEventPathSpecs.js ├── hasClassSpecs.js ├── ignoreSpecs.js ├── listenSpecs.js ├── modifyBemClassSpecs.js ├── oneOutOfSpecs.js ├── parentsSpecs.js ├── removeAttrSpecs.js ├── removeAttrsSpecs.js ├── removeClassSpecs.js ├── selfAndParentsSpecs.js ├── setAttrSpecs.js ├── setAttrsSpecs.js ├── timeSinceSpecs.js ├── toAveragePropSpecs.js ├── toAverageSpecs.js ├── toClosestPropertySpecs.js ├── toClosestSpecs.js ├── toLargestPropSpecs.js ├── toSmallestPropSpecs.js ├── toSumSpecs.js ├── triggerSpecs.js └── waitInPromiseSpecs.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 6, 9 | "sourceType": "module", 10 | "ecmaFeatures": { 11 | "jsx": true 12 | } 13 | }, 14 | "rules": { 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ], 19 | "semi": [ 20 | "warn", 21 | "always" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | node_modules/ 3 | temp/ 4 | docs/_site/ 5 | .sass-cache/ 6 | 7 | .DS_Store 8 | .jekyll-metadata 9 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "7" 5 | 6 | script: npm test 7 | 8 | before_install: 9 | - export CHROME_BIN=/usr/bin/google-chrome 10 | - export DISPLAY=:99.0 11 | - sh -e /etc/init.d/xvfb start 12 | - sudo apt-get update 13 | - sudo apt-get install -y libappindicator1 fonts-liberation 14 | - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 15 | - sudo dpkg -i google-chrome*.deb 16 | 17 | addons: 18 | firefox: latest 19 | chrome: stable 20 | 21 | sudo: required 22 | 23 | dist: trusty 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 2.3.0 — 2018-09-08 4 | 5 | ### Added 6 | - ES6 Imports. 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Canivete  4 | 5 | **[Documentation](https://canivete.leofavre.com/)** 6 | 7 | --- 8 | 9 | Canivete (Brazilian Portuguese for swiss army knife) is an on-going personal project by [Leonardo Favre](https://leofavre.com/), a place to keep generic, multi-purpose Javascript functions. 10 | 11 | Due to its multi-purpose nature, Canivete is not distributed as a single file, instead, one should import its functions as needed, using [ES6 module syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import). 12 | 13 | [Babel](https://babeljs.io/) and [Babel Polyfill](https://babeljs.io/docs/usage/polyfill/) are recommended to make Canivete work in older browsers. 14 | 15 | ## Usage 16 | 17 | Since ES6 modules have virtually no browser support at this time, a module bundler is needed to handle Canivete dependencies. 18 | 19 | Here's how to use Canivete with [Rollup](https://github.com/leofavre/canivete-with-rollup) and [Webpack](https://github.com/leofavre/canivete-with-webpack). 20 | 21 | ## Credits 22 | 23 | * **Canivete** Coding and documentation by [Leonardo Favre](https://leofavre.com/) 24 | * **Website** Coding and layout by [Leonardo Favre](https://leofavre.com/) and symbol by [Andre Levy a.k.a. Zhion](http://www.zhion.com.br) 25 | * **Typography** [Acropolis](https://www.typography.com/fonts/acropolis/overview/) and [Whitney](https://www.typography.com/fonts/whitney/overview/) by [Hoefler & Co.](https://www.typography.com/) and [Fira Code](https://github.com/tonsky/FiraCode) by [Nikita Prokopov](https://github.com/tonsky/) 26 | 27 | Canivete is released under The Unlicense license. 28 | -------------------------------------------------------------------------------- /dist/addClass.js: -------------------------------------------------------------------------------- 1 | import _changeClassWithMethod from "./internal/classname/_changeClassWithMethod.js"; 2 | import _domElementsToArray from "./internal/dom/_domElementsToArray.js"; 3 | 4 | /** 5 | * Adds a CSS class to one or more DOM elements. 6 | * 7 | * @category ClassName 8 | * 9 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 10 | * @param {string} className The CSS class name. 11 | * 12 | * @example 13 | * let oneElement = document.querySelector("a"); 14 | * addClass(oneElement, "link"); 15 | * 16 | * oneElement.className; 17 | * // => "link" 18 | * 19 | * @example 20 | * let manyElements = document.querySelectorAll("a"); 21 | * addClass(manyElements, "link"); 22 | * 23 | * manyElements[0].className; 24 | * // => "link" 25 | */ 26 | const addClass = (domEls, className) => { 27 | _domElementsToArray(domEls).forEach(domEl => _changeClassWithMethod(domEl, className, "add")); 28 | }; 29 | 30 | export default addClass; 31 | -------------------------------------------------------------------------------- /dist/afterFirst.js: -------------------------------------------------------------------------------- 1 | import _sliceOnOccurrence from "./internal/string/_sliceOnOccurrence.js"; 2 | 3 | /** 4 | * Returns the string formed by the characters **after 5 | * the first occurrence** of the delimiter in a base string. 6 | * If the delimiter is not found, the function returns `undefined`. 7 | * 8 | * @category String 9 | * 10 | * @param {string} str The base string. 11 | * @param {string} delimiter The delimiter string. 12 | * @return {string} 13 | * @public 14 | * 15 | * @example 16 | * afterFirst("parallelepiped", "le"); 17 | * // => "lepiped" 18 | */ 19 | const afterFirst = (str, delimiter) => _sliceOnOccurrence("after", "first", str, delimiter); 20 | 21 | export default afterFirst; 22 | -------------------------------------------------------------------------------- /dist/afterLast.js: -------------------------------------------------------------------------------- 1 | import _sliceOnOccurrence from "./internal/string/_sliceOnOccurrence.js"; 2 | 3 | /** 4 | * Returns the string formed by the characters **after 5 | * the last occurrence** of the delimiter in a base string. 6 | * If the delimiter is not found, the function returns `undefined`. 7 | * 8 | * @category String 9 | * 10 | * @param {string} str The base string. 11 | * @param {string} delimiter The delimiter string. 12 | * @return {string} 13 | * @public 14 | * 15 | * @example 16 | * afterLast("parallelepiped", "le"); 17 | * // => "piped" 18 | */ 19 | const afterLast = (str, delimiter) => _sliceOnOccurrence("after", "last", str, delimiter); 20 | 21 | export default afterLast; 22 | -------------------------------------------------------------------------------- /dist/beforeFirst.js: -------------------------------------------------------------------------------- 1 | import _sliceOnOccurrence from "./internal/string/_sliceOnOccurrence.js"; 2 | 3 | /** 4 | * Returns the string formed by the characters **before 5 | * the first occurrence** of the delimiter in a base string. 6 | * If the delimiter is not found, the function returns `undefined`. 7 | * 8 | * @category String 9 | * 10 | * @param {string} str The base string. 11 | * @param {string} delimiter The delimiter string. 12 | * @return {string} 13 | * @public 14 | * 15 | * @example 16 | * beforeFirst("parallelepiped", "le"); 17 | * // => "paral" 18 | */ 19 | const beforeFirst = (str, delimiter) => _sliceOnOccurrence("before", "first", str, delimiter); 20 | 21 | export default beforeFirst; 22 | -------------------------------------------------------------------------------- /dist/beforeLast.js: -------------------------------------------------------------------------------- 1 | import _sliceOnOccurrence from "./internal/string/_sliceOnOccurrence.js"; 2 | 3 | /** 4 | * Returns the string formed by the characters **before 5 | * the last occurrence** of the delimiter in a base string. 6 | * If the delimiter is not found, the function returns `undefined`. 7 | * 8 | * @category String 9 | * 10 | * @param {string} str The base string. 11 | * @param {string} delimiter The delimiter string. 12 | * @return {string} 13 | * @public 14 | * 15 | * @example 16 | * beforeLast("parallelepiped", "le"); 17 | * // => "paralle" 18 | */ 19 | const beforeLast = (str, delimiter) => _sliceOnOccurrence("before", "last", str, delimiter); 20 | 21 | export default beforeLast; 22 | -------------------------------------------------------------------------------- /dist/byAlphabeticalOrder.js: -------------------------------------------------------------------------------- 1 | import _defaultComparator from "./internal/sort/_defaultComparator.js"; 2 | 3 | /** 4 | * When used with `[].sort()`, sorts 5 | * the array in ascending alphabetical order. 6 | * 7 | * Note that the parentheses can be ommited. 8 | * 9 | * @category Sort 10 | * 11 | * @return {Array} The array in ascending alphabetical order. 12 | * 13 | * @example 14 | * let musqueteers = ["Athos", "Porthos", "Aramis"]; 15 | * 16 | * musqueteers.sort(byAlphabeticalOrder()); 17 | * // => ["Aramis", "Athos", "Porthos"] 18 | * 19 | * musqueteers.sort(byAlphabeticalOrder); 20 | * // => ["Aramis", "Athos", "Porthos"] 21 | */ 22 | const byAlphabeticalOrder = (...args) => 23 | (args.length === 0) ? _defaultComparator : _defaultComparator(...args); 24 | 25 | export default byAlphabeticalOrder; 26 | -------------------------------------------------------------------------------- /dist/byProps.js: -------------------------------------------------------------------------------- 1 | import _recursiveSort from "./internal/sort/_recursiveSort.js"; 2 | import _parseSortFields from "./internal/sort/_parseSortFields.js"; 3 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 4 | 5 | /** 6 | * @typedef {Object} SortField 7 | * @property {string} path The path to the property of an object. 8 | * @property {Function} [primer] The function used to process each value before sorting. 9 | * @property {boolean} [reverse] Whether the result should be reversed. 10 | */ 11 | 12 | /** 13 | * When used with `[].sort()`, returns an array of 14 | * objects sorted by one or more criteria, passed as 15 | * parameters. 16 | * 17 | * Each parameter can be eitheir a path to an object 18 | * property, passed as a string, or an object containing 19 | * a path to an object property, a boolean value indicating 20 | * if the result should be reversed, and a function to 21 | * process each value before sorting. 22 | * 23 | * @category Sort 24 | * 25 | * @param {...(string|SortField)} ...fields The criteria used to sort the array of objects. 26 | * @return {Array.} The resulting array. 27 | * 28 | * @example 29 | * let places = [{ 30 | * "name": "Ipanema", 31 | * "location": { 32 | * "city": "Rio de Janeiro", 33 | * "state": "RJ" 34 | * } 35 | * }, { 36 | * "name": "Pedras", 37 | * "location": { 38 | * "city": "Búzios", 39 | * "state": "RJ" 40 | * } 41 | * }, { 42 | * "name": "Morumbi", 43 | * "location": { 44 | * "city": "São Paulo", 45 | * "state": "SP" 46 | * } 47 | * }]; 48 | * 49 | * places.sort(byProps("name")); 50 | * // Sorts places by name 51 | * // => [ 52 | * // => { "name": "Ipanema", [...] }, 53 | * // => { "name": "Morumbi", [...] }, 54 | * // => { "name": "Pedras", [...] } 55 | * // => ] 56 | * 57 | * places.sort(byProps({ "path": "name", "reverse": true }); 58 | * // Sorts places by name in reversed order 59 | * // => [ 60 | * // => { "name": "Pedras", [...] }, 61 | * // => { "name": "Morumbi", [...] }, 62 | * // => { "name": "Ipanema", [...] } 63 | * // => ] 64 | * 65 | * places.sort(byProps("location.state", "location.city", "name")); 66 | * // Sorts places by state, city and name 67 | * // => [ 68 | * // => { "name": "Pedras", [...] }, 69 | * // => { "name": "Ipanema", [...] }, 70 | * // => { "name": "Morumbi", [...] } 71 | * // => ] 72 | * 73 | * places.sort(byProps({ "path": "location.state", "reverse": true }, "location.city", "name")); 74 | * // Sorts places by state (in reversed order), city and name 75 | * // => [ 76 | * // => { "name": "Morumbi", [...] }, 77 | * // => { "name": "Pedras", [...] }, 78 | * // => { "name": "Ipanema", [...] } 79 | * // => ] 80 | * 81 | * @example 82 | * let numbers = [{ 83 | * "value": 35 84 | * }, { 85 | * "value": -20 86 | * }, { 87 | * "value": 3 88 | * }, { 89 | * "value": 0.8 90 | * }]; 91 | * 92 | * numbers.sort(byProps("value")); 93 | * // Sorts numbers by value in ascending order 94 | * // => [{ "value": -20 }, { "value": 0.8 }, { "value": 3 }, { "value": 35 }] 95 | * 96 | * numbers.sort(byProps({ "path": "value", "primer": Math.abs })); 97 | * // Sorts numbers by value in ascending order but ignoring the minus sign 98 | * // => [{ "value": 0.8 }, { "value": 3 }, { "value": -20 }, { "value": 35 }] 99 | */ 100 | const byProps = (...fields) => { 101 | _throwErrorIf(fields.length === 0, "One or more sort criterias should be passed as parameters."); 102 | return _recursiveSort(_parseSortFields(fields)); 103 | }; 104 | 105 | export default byProps; 106 | -------------------------------------------------------------------------------- /dist/deepGroupBy.js: -------------------------------------------------------------------------------- 1 | import _simpleAt from "./internal/helpers/_simpleAt.js"; 2 | import _simpleSet from "./internal/helpers/_simpleSet.js"; 3 | 4 | /** 5 | * Groups the contents of an array by one or more iteratees. 6 | * This function is similar to Lodash 7 | * [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy), 8 | * except it can create nested groups but cannot receive 9 | * strings for iteratees. 10 | * 11 | * @category Collection 12 | * 13 | * @param {Array} collection The original array. 14 | * @param {...Function} [...iteratees] The functions used to group the array of objects by their results. 15 | * @return {Object} The resulting object. 16 | * 17 | * @example 18 | * 19 | * const getLength = str => str.length; 20 | * const getFirstLetter = str => str.slice(0, 1); 21 | * 22 | * deepGroupBy(["one", "two", "three"], getLength, getFirstLetter); 23 | * // => { 24 | * // => "3": {"o": ["one"], "t": ["two"]}, 25 | * // => "5": {"t": ["three"]} 26 | * // => } 27 | * 28 | * @example 29 | * 30 | * const getLength = str => str.length; 31 | * const getFirstLetter = str => str.slice(0, 1); 32 | * 33 | * deepGroupBy(["one", "two", "three"], getFirstLetter, getLength); 34 | * // => { 35 | * // => "o": {"3": ["one"]}, 36 | * // => "t": {"3": ["two"], "5": ["three"]} 37 | * // => } 38 | * 39 | * @example 40 | * 41 | * const stores = [{ 42 | * "name": "Iguatemi", 43 | * "city": "Campinas", 44 | * "state": "SP" 45 | * }, { 46 | * "name": "Jardins", 47 | * "city": "São Paulo", 48 | * "state": "SP" 49 | * }, { 50 | * "name": "Iguatemi", 51 | * "city": "São Paulo", 52 | * "state": "SP" 53 | * }, { 54 | * "name": "Pedras", 55 | * "city": "Búzios", 56 | * "state": "RJ" 57 | * }, { 58 | * "name": "Ipanema", 59 | * "city": "Rio de Janeiro", 60 | * "state": "RJ" 61 | * }, { 62 | * "name": "Leblon", 63 | * "city": "Rio de Janeiro", 64 | * "state": "RJ" 65 | * }, { 66 | * "name": "ParkShopping", 67 | * "city": "Brasília", 68 | * "state": "DF" 69 | * }]; 70 | * 71 | * const getStateName = item => item.state; 72 | * const getCityName = item => item.city; 73 | * 74 | * deepGroupBy(stores, getStateName, getCityName); 75 | * // => { 76 | * // => "SP": { "Campinas": [...], "São Paulo": [...] }, 77 | * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] }, 78 | * // => "DF": { "Brasília": [...] } 79 | * // => } 80 | */ 81 | const deepGroupBy = (collection, ...iteratees) => { 82 | let paths = collection.map(value => iteratees.map(iteratee => iteratee(value))), 83 | result = {}; 84 | 85 | paths.forEach((path, index) => { 86 | let currentValue = _simpleAt(result, path) || [], 87 | newValue = currentValue.concat([collection[index]]); 88 | 89 | _simpleSet(result, path, newValue); 90 | }); 91 | 92 | return result; 93 | }; 94 | 95 | export default deepGroupBy; 96 | -------------------------------------------------------------------------------- /dist/eventAsPromise.js: -------------------------------------------------------------------------------- 1 | import _isString from "./internal/helpers/_isString.js"; 2 | import _isElementOrDocumentOrWindow from "./internal/dom/_isElementOrDocumentOrWindow.js"; 3 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 4 | 5 | /** 6 | * Transforms a DOM event into a promise. 7 | * 8 | * The functions takes as parameters: a DOM element, 9 | * the name of the event to be listened for 10 | * and a function that verifies if the event has already 11 | * happened, which receives the DOM element as parameter. 12 | * 13 | * Like all promises in Javascript, the function will 14 | * only fulfill once, either if the verification function 15 | * returns true or when the event occurs for the first time. 16 | * 17 | * Note that the function throws an error if the first two 18 | * parameters are not a DOM element and a string. 19 | * 20 | * @category Promise 21 | * 22 | * @param {(Window|HTMLDocument|HTMLElement)} domEl The DOM element, including `document` and `window`. 23 | * @param {string} evtName The event to be listened for. 24 | * @param {function} [happened = domEl => false] The verification function. 25 | * @return {Promise} When fulfilled, returns the DOM element. 26 | * 27 | * @example 28 | * let checkbox = document.createElement("input"); 29 | * checkbox.type = "checkbox"; 30 | * document.body.appendChild(checkbox); 31 | * 32 | * eventAsPromise(checkbox, "change") 33 | * .then(doSomethingAfterChange); 34 | * 35 | * @example 36 | * let imageEl = document.createElement("img"); 37 | * imageEl.src = "img.jpg"; 38 | * document.body.appendChild(imageEl); 39 | * 40 | * eventAsPromise(imageEl, "load", imageEl => imageEl.complete) 41 | * .then(doSomethingAfterImageLoaded); 42 | */ 43 | const eventAsPromise = (domEl, evtName, happened = domEl => false) => { 44 | _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement, document or window are expected as first parameter."); 45 | _throwErrorIf(!_isString(evtName), "A string is expected as second parameter."); 46 | 47 | return new Promise(resolve => { 48 | const dealWithEvent = evt => { 49 | domEl.removeEventListener(evtName, dealWithEvent, true); 50 | resolve(domEl); 51 | }; 52 | 53 | if (happened(domEl)) { 54 | resolve(domEl); 55 | } 56 | else { 57 | domEl.addEventListener(evtName, dealWithEvent, true); 58 | } 59 | }); 60 | }; 61 | 62 | export default eventAsPromise; 63 | -------------------------------------------------------------------------------- /dist/formatBemClass.js: -------------------------------------------------------------------------------- 1 | import _formatBemRadical from "./internal/bem/_formatBemRadical.js"; 2 | import _formatBemModifier from "./internal/bem/_formatBemModifier.js"; 3 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 4 | 5 | /** 6 | * Formats a CSS class according to the 7 | * [BEM methodology](https://en.bem.info/methodology/). 8 | * The function receives a block, an element, a modifier, a value 9 | * for the modifier and an array of BEM delimiters, e.g. `__`, 10 | * `--` and `-`. 11 | * 12 | * @category BEM 13 | * 14 | * @param {string} block The BEM block. 15 | * @param {string} [element] The BEM element. 16 | * @param {string} [modifier] The BEM modifier. 17 | * @param {(string|number|boolean)} [value = true] The BEM modifier value. 18 | * @param {Array.} delimiters The BEM delimiters. 19 | * @return {string} The BEM CSS class. 20 | * 21 | * @example 22 | * let delimiters = ["__", "--", "-"]; 23 | * 24 | * formatBemClass("menu", delimiters); 25 | * // => "menu" 26 | * 27 | * formatBemClass("menu", "item", delimiters); 28 | * // => "menu__item" 29 | * 30 | * formatBemClass("menu", "item", "active", delimiters); 31 | * // => "menu__item--active" 32 | * 33 | * formatBemClass("menu", "item", "active", false, delimiters); 34 | * // => "menu__item" 35 | * 36 | * formatBemClass("menu", "item", "active", true, delimiters); 37 | * // => "menu__item--active" 38 | * 39 | * formatBemClass("menu", "item", "level", 42, delimiters); 40 | * // => "menu__item--level-42" 41 | * 42 | * formatBemClass("menu", "item", "level", "42", delimiters); 43 | * // => "menu__item--level-42" 44 | * 45 | * @example 46 | * let delimiters = ["__", "--", "-"]; 47 | * 48 | * formatBemClass("button", null, "active", delimiters); 49 | * // => "button--active" 50 | * 51 | * formatBemClass("button", null, "active", false, delimiters); 52 | * // => "button" 53 | * 54 | * formatBemClass("button", null, "active", true, delimiters); 55 | * // => "button--active" 56 | * 57 | * formatBemClass("button", null, "level", 42, delimiters); 58 | * // => "button--level-42" 59 | * 60 | * formatBemClass("button", null, "level", "42", delimiters); 61 | * // => "button--level-42" 62 | */ 63 | const formatBemClass = (...args) => { 64 | let block = args[0] || ""; 65 | let delimiters = args[args.length - 1]; 66 | 67 | _throwErrorIf((args.length < 2), "At least a string representing a BEM block and an array representing BEM delimiters should be passed as parameters."); 68 | 69 | let element, 70 | modifier, 71 | value = true; 72 | 73 | if (args.length > 2) { 74 | element = args[1]; 75 | } 76 | 77 | if (args.length > 3) { 78 | modifier = args[2]; 79 | } 80 | 81 | if (args.length > 4) { 82 | value = args[3]; 83 | } 84 | 85 | let radical = _formatBemRadical(block, element, delimiters); 86 | let classModifier = _formatBemModifier(modifier, value, delimiters); 87 | 88 | return `${radical}${classModifier}`; 89 | }; 90 | 91 | export default formatBemClass; 92 | -------------------------------------------------------------------------------- /dist/getAttr.js: -------------------------------------------------------------------------------- 1 | import _isBooleanAttribute from "./internal/dom/_isBooleanAttribute.js"; 2 | 3 | /** 4 | * Gets a DOM element attribute using native 5 | * [`Element.getAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute), 6 | * except that the presence of an attribute without 7 | * a value will return `true` instead of an empty string, 8 | * and the absence will return `false`. The function also 9 | * deals with boolean values using old HTML4 syntax, 10 | * like ``. 11 | * 12 | * @category DOM 13 | * 14 | * @param {HTMLElement} domEl The DOM element. 15 | * @param {string} attrName The attribute name. 16 | * @return {(string|Boolean)} The attribute value. 17 | * 18 | * @example 19 | * // HTML5 syntax 20 | * let inputElement = createDomElement(''); 21 | * 22 | * getAttr(inputElement, "checked"); 23 | * // => true 24 | * 25 | * @example 26 | * // HTML4 syntax 27 | * let inputElement = createDomElement(''); 28 | * 29 | * getAttr(inputElement, "checked"); 30 | * // => true 31 | * 32 | * @example 33 | * let videoElement = createDomElement(''); 34 | * 35 | * getAttr(videoElement, "src"); 36 | * // => "video.mp4" 37 | * 38 | * getAttr(videoElement, "controls"); 39 | * // => true 40 | * 41 | * getAttr(videoElement, "muted"); 42 | * // => false 43 | */ 44 | const getAttr = (domEl, attrName) => { 45 | let value = domEl.getAttribute(attrName); 46 | 47 | if (_isBooleanAttribute(attrName) && attrName === value) { 48 | value = true; 49 | } 50 | 51 | value = (value === "") ? true : (value == null) ? false : value; 52 | return value; 53 | }; 54 | 55 | export default getAttr; 56 | -------------------------------------------------------------------------------- /dist/getDistanceBetweenCoords.js: -------------------------------------------------------------------------------- 1 | import toSum from "./toSum.js"; 2 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 3 | 4 | /** 5 | * Calculates and returns the distance between two points, 6 | * given their cartesian coordinates, represented, each one, 7 | * by an array of numbers. 8 | * 9 | * For example, the point in a plane A(x, y) should be passed 10 | * to the function as `[x, y]`. Likewise, the point in 11 | * 3D space A(x, y, z) should be passed as `[x, y, z]`. 12 | * 13 | * The function deals with cartesian coordinates in 14 | * [n-dimensional spaces](https://en.wikipedia.org/wiki/Euclidean_distance#n_dimensions). 15 | * 16 | * @category Geometry 17 | * @param {Array.} coordA An array representing a cartesian coordinate. 18 | * @param {Array.} coordB An array representing a cartesian coordinate. 19 | * @return {number} The distance between the two cartesian coordinates. 20 | * 21 | * @example 22 | * getDistanceBetweenCoords([0, 0], [3, 4]); 23 | * // => 5 24 | * 25 | * getDistanceBetweenCoords([2, 1], [5, 5]); 26 | * // => 5 27 | * 28 | * getDistanceBetweenCoords([2, 1, 8], [5, 5, 0]); 29 | * // => 9.433981132056603 30 | * 31 | * getDistanceBetweenCoords([2], [5]); 32 | * // => 3 33 | */ 34 | const getDistanceBetweenCoords = (coordA, coordB) => { 35 | let areParamsValid = [coordA, coordB] 36 | .every(coord => Array.isArray(coord) && 37 | coord.every(Number.isFinite) && 38 | coord.length === coordA.length); 39 | 40 | _throwErrorIf(!areParamsValid, "Two arrays of numbers with the same length, representing cartesian coordinates, are expected as parameters."); 41 | 42 | return Math.sqrt(coordA 43 | .map((coord, index) => Math.pow(coord - coordB[index], 2)) 44 | .reduce(toSum)); 45 | }; 46 | 47 | export default getDistanceBetweenCoords; -------------------------------------------------------------------------------- /dist/getEventPath.js: -------------------------------------------------------------------------------- 1 | import selfAndParents from "./selfAndParents.js"; 2 | 3 | /** 4 | * Returns an array with all DOM elements affected by an event. 5 | * The function serves as a polyfill for 6 | * [`Event.composedPath()`](https://dom.spec.whatwg.org/#dom-event-composedpath). 7 | * 8 | * @category Event 9 | * @param {Event} evt The triggered event. 10 | * @return {Array.} The DOM elements affected by the event. 11 | * 12 | * @example 13 | * let domChild = document.createElement("div"), 14 | * domParent = document.createElement("div"), 15 | * domGrandparent = document.createElement("div"), 16 | * body = document.body, 17 | * html = document.querySelector("html"); 18 | * 19 | * domParent.appendChild(domChild); 20 | * domGrandparent.appendChild(domParent); 21 | * body.appendChild(domGrandparent); 22 | * 23 | * domChild.addEventListener("click", dealWithClick); 24 | * const dealWithClick = evt => getEventPath(evt); 25 | * 26 | * // when domChild is clicked: 27 | * // => [domChild, domParent, domGrandparent, body, html, document, window] 28 | */ 29 | const getEventPath = evt => { 30 | let path = (evt.composedPath && evt.composedPath()) || evt.path, 31 | target = evt.target; 32 | 33 | if (target == null) { 34 | return undefined; 35 | } 36 | 37 | if (path != null) { 38 | path = (!path.includes(window)) ? path.concat([window]) : path; 39 | return path; 40 | } 41 | 42 | if (target === window) { 43 | return [window]; 44 | } 45 | 46 | return selfAndParents(target).concat([window]); 47 | }; 48 | 49 | export default getEventPath; 50 | -------------------------------------------------------------------------------- /dist/hasClass.js: -------------------------------------------------------------------------------- 1 | import _isElement from "./internal/helpers/_isElement.js"; 2 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 3 | 4 | /** 5 | * Verifies if a DOM element has a CSS class. 6 | * 7 | * @category ClassName 8 | * 9 | * @param {HTMLElement} domEl The DOM element. 10 | * @param {string} className The CSS class name. 11 | * @return {boolean} Whether the element has the CSS class name. 12 | * 13 | * @example 14 | * let oneElement = document.querySelector("a"); 15 | * oneElement.className = "link reference"; 16 | * 17 | * hasClass(oneElement, "link"); 18 | * // => true 19 | * 20 | * hasClass(oneElement, "button"); 21 | * // => false 22 | */ 23 | const hasClass = (domEl, className) => { 24 | _throwErrorIf(!_isElement(domEl), "An HTMLElement is expected as parameter."); 25 | return domEl.classList.contains(className); 26 | }; 27 | 28 | export default hasClass; 29 | -------------------------------------------------------------------------------- /dist/ignore.js: -------------------------------------------------------------------------------- 1 | import _reactToEvent from "./internal/event/_reactToEvent.js"; 2 | 3 | /** 4 | * Removes one or more event listeners from one or more DOM elements at once. 5 | * The function is a wrapper for 6 | * [`Element.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) 7 | * that accepts a space-separated event names string and a group 8 | * of target DOM elements. 9 | * 10 | * @category Event 11 | * @param {(Window|HTMLDocument|HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements, including `document` and `window`. 12 | * @param {string} eventStr The event names string. 13 | * @param {Function} callback The function to be ignored. 14 | * @param {Boolean} [useCapture = false] The event phase being listened for. 15 | * 16 | * @example 17 | * 18 | * let oneElement = document.querySelector("a"); 19 | * let manyElements = document.querySelectorAll("a"); 20 | * 21 | * ignore(oneElement, "click", reactToClick, true); 22 | * ignore(manyElements, "click", reactToClick); 23 | * ignore(window, "load resize", reactToLoadAndResize); 24 | */ 25 | const ignore = (domEls, eventStr, callback, useCapture = false) => _reactToEvent("removeEventListener", domEls, eventStr, callback, useCapture); 26 | 27 | export default ignore; 28 | -------------------------------------------------------------------------------- /dist/internal/bem/_formatBemClassFromRadical.js: -------------------------------------------------------------------------------- 1 | import _formatBemModifier from "./_formatBemModifier.js"; 2 | 3 | function _formatBemClassFromRadical(radical, modifier, value, delimiters) { 4 | let classModifier = _formatBemModifier(modifier, value, delimiters); 5 | return `${radical}${classModifier}`; 6 | } 7 | 8 | export default _formatBemClassFromRadical; 9 | -------------------------------------------------------------------------------- /dist/internal/bem/_formatBemModifier.js: -------------------------------------------------------------------------------- 1 | import _joinBemEntityWithDelimiter from "./_joinBemEntityWithDelimiter.js"; 2 | 3 | function _formatBemModifier(modifier, value, delimiters) { 4 | let modifierDelimiter = delimiters[1], 5 | modifierBase = _joinBemEntityWithDelimiter(modifier, modifierDelimiter); 6 | 7 | let valueDelimiter = delimiters[2], 8 | valueBase = _joinBemEntityWithDelimiter(value, valueDelimiter); 9 | 10 | return (value === true) ? modifierBase : (value !== false && value != null) ? `${modifierBase}${valueBase}` : ""; 11 | } 12 | 13 | export default _formatBemModifier; 14 | -------------------------------------------------------------------------------- /dist/internal/bem/_formatBemRadical.js: -------------------------------------------------------------------------------- 1 | import _joinBemEntityWithDelimiter from "./_joinBemEntityWithDelimiter.js"; 2 | 3 | function _formatBemRadical(block, element, delimiters) { 4 | let elementDelimiter = delimiters[0], 5 | elementBase = _joinBemEntityWithDelimiter(element, elementDelimiter); 6 | 7 | return `${block}${elementBase}`; 8 | } 9 | 10 | export default _formatBemRadical; 11 | -------------------------------------------------------------------------------- /dist/internal/bem/_hasAllModifiersSetToFalse.js: -------------------------------------------------------------------------------- 1 | const _hasAllModifiersSetToFalse = modifierObj => { 2 | return Object.keys(modifierObj).every(key => modifierObj[key] === false); 3 | }; 4 | 5 | export default _hasAllModifiersSetToFalse; 6 | -------------------------------------------------------------------------------- /dist/internal/bem/_hasNoModifiers.js: -------------------------------------------------------------------------------- 1 | const _hasNoModifiers = modifierObj => Object.keys(modifierObj).length === 0; 2 | 3 | export default _hasNoModifiers; 4 | -------------------------------------------------------------------------------- /dist/internal/bem/_isValidBemObject.js: -------------------------------------------------------------------------------- 1 | import _isValidModifierObject from "./_isValidModifierObject.js"; 2 | import _isPlainObject from "../helpers/_isPlainObject.js"; 3 | 4 | const _isValidBemObject = bemObj => { 5 | return _isPlainObject(bemObj) && Object.keys(bemObj).every(key => _isValidModifierObject(bemObj[key])); 6 | }; 7 | 8 | export default _isValidBemObject; 9 | -------------------------------------------------------------------------------- /dist/internal/bem/_isValidDelimiterArray.js: -------------------------------------------------------------------------------- 1 | import _isString from "../helpers/_isString.js"; 2 | 3 | const _isValidDelimiterArray = delimiters => { 4 | return Array.isArray(delimiters) && delimiters.length === 3 && delimiters.every(delimiter => _isString(delimiter) && delimiter !== ""); 5 | }; 6 | 7 | export default _isValidDelimiterArray; 8 | -------------------------------------------------------------------------------- /dist/internal/bem/_isValidModifierObject.js: -------------------------------------------------------------------------------- 1 | import _isString from "../helpers/_isString.js"; 2 | import _isBoolean from "../helpers/_isBoolean.js"; 3 | import _isPlainObject from "../helpers/_isPlainObject.js"; 4 | 5 | const _isValidModifierObject = modifierObj => { 6 | return _isPlainObject(modifierObj) && Object.keys(modifierObj).every(key => { 7 | let prop = modifierObj[key]; 8 | return _isBoolean(prop) || _isString(prop) || Number.isFinite(prop); 9 | }); 10 | }; 11 | 12 | export default _isValidModifierObject; 13 | -------------------------------------------------------------------------------- /dist/internal/bem/_joinBemEntityWithDelimiter.js: -------------------------------------------------------------------------------- 1 | const _joinBemEntityWithDelimiter = (entity, delimiter) => { 2 | return (entity != null && entity !== "" && typeof entity !== "boolean") ? `${delimiter}${entity}` : ""; 3 | }; 4 | 5 | export default _joinBemEntityWithDelimiter; 6 | -------------------------------------------------------------------------------- /dist/internal/bem/_parseBem.js: -------------------------------------------------------------------------------- 1 | import _parseBemEntityWithFunc from "./_parseBemEntityWithFunc.js"; 2 | import _parseBemProp from "./_parseBemProp.js"; 3 | 4 | const _parseBem = (...args) => { 5 | return _parseBemEntityWithFunc(_parseBemProp, ...args); 6 | }; 7 | 8 | export default _parseBem; 9 | -------------------------------------------------------------------------------- /dist/internal/bem/_parseBemEntityWithFunc.js: -------------------------------------------------------------------------------- 1 | const _parseBemEntityWithFunc = (func, ...args) => Object.keys(args[0]).forEach(func(...args)); 2 | 3 | export default _parseBemEntityWithFunc; 4 | -------------------------------------------------------------------------------- /dist/internal/bem/_parseBemProp.js: -------------------------------------------------------------------------------- 1 | import addClass from "../../addClass.js"; 2 | import removeClass from "../../removeClass.js"; 3 | import _hasNoModifiers from "./_hasNoModifiers.js"; 4 | import _hasAllModifiersSetToFalse from "./_hasAllModifiersSetToFalse.js"; 5 | import _parseModifier from "./_parseModifier.js"; 6 | 7 | function _parseBemProp(bemObj, domEl, delimiters, shouldRepeatBemRadical) { 8 | return function(radical) { 9 | let modifierObj = bemObj[radical]; 10 | 11 | if (shouldRepeatBemRadical || _hasNoModifiers(modifierObj) || _hasAllModifiersSetToFalse(modifierObj)) { 12 | addClass(domEl, radical); 13 | } 14 | else { 15 | removeClass(domEl, radical); 16 | } 17 | 18 | _parseModifier(modifierObj, domEl, radical, delimiters); 19 | }; 20 | } 21 | 22 | export default _parseBemProp; 23 | -------------------------------------------------------------------------------- /dist/internal/bem/_parseModifier.js: -------------------------------------------------------------------------------- 1 | import _parseBemEntityWithFunc from "./_parseBemEntityWithFunc.js"; 2 | import _parseModifierProp from "./_parseModifierProp.js"; 3 | 4 | const _parseModifier = (...args) => _parseBemEntityWithFunc(_parseModifierProp, ...args); 5 | 6 | export default _parseModifier; 7 | -------------------------------------------------------------------------------- /dist/internal/bem/_parseModifierProp.js: -------------------------------------------------------------------------------- 1 | import addClass from "../../addClass.js"; 2 | import removeClass from "../../removeClass.js"; 3 | import _removeClassesBeginningWithButNot from "../classname/_removeClassesBeginningWithButNot.js"; 4 | import formatBemClass from "../../formatBemClass.js"; 5 | 6 | function _parseModifierProp(modifierObj, domEl, radical, delimiters) { 7 | return function(modifier) { 8 | let blockDelimiter = delimiters[0], 9 | block = radical.split(blockDelimiter)[0], 10 | element = radical.split(blockDelimiter)[1]; 11 | 12 | let value = modifierObj[modifier]; 13 | 14 | let removedBemClass = formatBemClass(block, element, modifier, true, delimiters), 15 | addedBemClass = formatBemClass(block, element, modifier, value, delimiters); 16 | 17 | if (value === false) { 18 | removeClass(domEl, removedBemClass); 19 | } 20 | else if (value !== true) { 21 | _removeClassesBeginningWithButNot(domEl, removedBemClass); 22 | } 23 | 24 | if (value !== false) { 25 | addClass(domEl, addedBemClass); 26 | } 27 | }; 28 | } 29 | 30 | export default _parseModifierProp; 31 | -------------------------------------------------------------------------------- /dist/internal/bem/_validateModifyBemClassArgs.js: -------------------------------------------------------------------------------- 1 | import _isElement from "../helpers/_isElement.js"; 2 | import _isValidBemObject from "./_isValidBemObject.js"; 3 | import _isValidDelimiterArray from "./_isValidDelimiterArray.js"; 4 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 5 | 6 | const _validateModifyBemClassArgs = (domEl, bemObj, delimiters) => { 7 | _throwErrorIf(!_isElement(domEl), `An HTMLElement is expected as the first parameter.`); 8 | _throwErrorIf(!_isValidBemObject(bemObj), `An object describing BEM class changes is expected as the second parameter.`); 9 | _throwErrorIf(!_isValidDelimiterArray(delimiters), `An array with three strings representing BEM delimiters is expected as the third parameter.`); 10 | }; 11 | 12 | export default _validateModifyBemClassArgs; 13 | -------------------------------------------------------------------------------- /dist/internal/classname/_changeClassWithMethod.js: -------------------------------------------------------------------------------- 1 | const _changeClassWithMethod = (domEl, str, methodName) => { 2 | if (str) { 3 | domEl.classList[methodName](str); 4 | } 5 | }; 6 | 7 | export default _changeClassWithMethod; 8 | -------------------------------------------------------------------------------- /dist/internal/classname/_removeClassesBeginningWithButNot.js: -------------------------------------------------------------------------------- 1 | import _removeClassesBeginningWithButNotBase from "./_removeClassesBeginningWithButNotBase.js"; 2 | import _domElementsToArray from "../dom/_domElementsToArray.js"; 3 | 4 | const _removeClassesBeginningWithButNot = (domEls, str) => { 5 | _domElementsToArray(domEls).forEach(domEl => _removeClassesBeginningWithButNotBase(domEl, str)); 6 | }; 7 | 8 | export default _removeClassesBeginningWithButNot; 9 | -------------------------------------------------------------------------------- /dist/internal/classname/_removeClassesBeginningWithButNotBase.js: -------------------------------------------------------------------------------- 1 | import removeClass from "../../removeClass.js"; 2 | 3 | const _removeClassesBeginningWithButNotBase = (domEl, str) => { 4 | for (let i = domEl.classList.length - 1; i >= 0; i--) { 5 | let currentClass = domEl.classList.item(i); 6 | 7 | if (currentClass !== str && currentClass.startsWith(str)) { 8 | removeClass(domEl, currentClass); 9 | } 10 | } 11 | return domEl; 12 | }; 13 | 14 | export default _removeClassesBeginningWithButNotBase; 15 | -------------------------------------------------------------------------------- /dist/internal/dom/_createDomElement.js: -------------------------------------------------------------------------------- 1 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 2 | 3 | const _createDomElement = htmlStr => { 4 | let domEl = document.createElement("div"); 5 | domEl.innerHTML = htmlStr; 6 | 7 | _throwErrorIf(domEl.children.length !== 1, "A string defining a single HTMLElement, with or without children, is expected as parameter."); 8 | 9 | return domEl.children[0]; 10 | }; 11 | 12 | export default _createDomElement; 13 | -------------------------------------------------------------------------------- /dist/internal/dom/_createFragment.js: -------------------------------------------------------------------------------- 1 | const _createFragment = htmlStr => { 2 | let domEl = document.createElement("div"), 3 | result = document.createDocumentFragment(); 4 | 5 | domEl.innerHTML = htmlStr; 6 | 7 | Array.from(domEl.children).forEach(child => { 8 | result.appendChild(child); 9 | }); 10 | 11 | return result; 12 | }; 13 | 14 | export default _createFragment; 15 | -------------------------------------------------------------------------------- /dist/internal/dom/_domElementsOrDocumentOrWindowToArray.js: -------------------------------------------------------------------------------- 1 | import _domToArray from "./_domToArray.js"; 2 | 3 | const _domElementsOrDocumentOrWindowToArray = arg => _domToArray(arg, true); 4 | 5 | export default _domElementsOrDocumentOrWindowToArray; 6 | -------------------------------------------------------------------------------- /dist/internal/dom/_domElementsToArray.js: -------------------------------------------------------------------------------- 1 | import _domToArray from "./_domToArray.js"; 2 | 3 | const _domElementsToArray = arg => _domToArray(arg, false); 4 | 5 | export default _domElementsToArray; 6 | -------------------------------------------------------------------------------- /dist/internal/dom/_domToArray.js: -------------------------------------------------------------------------------- 1 | import _isElement from "../helpers/_isElement.js"; 2 | import _isElementOrDocumentOrWindow from "./_isElementOrDocumentOrWindow.js"; 3 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 4 | 5 | const _domElementsToArray = (arg, allowDocumentAndWindow = false) => { 6 | let result = arg, 7 | isDom = allowDocumentAndWindow ? _isElementOrDocumentOrWindow : _isElement, 8 | errorMsg = allowDocumentAndWindow ? "One or more HTMLElements are expected as the first parameter." : "One or more HTMLElements, including document or window, are expected as the first parameter."; 9 | 10 | if (arg == null) { 11 | return []; 12 | } 13 | 14 | if (isDom(arg)) { 15 | result = [arg]; 16 | } 17 | else if (arg != null && (typeof arg.length === "number" || typeof arg.size === "number")) { 18 | result = Array.from(arg); 19 | } 20 | 21 | _throwErrorIf(!Array.isArray(result) || result.some(item => !isDom(item)), errorMsg); 22 | 23 | return result; 24 | }; 25 | 26 | export default _domElementsToArray; 27 | -------------------------------------------------------------------------------- /dist/internal/dom/_isBooleanAttribute.js: -------------------------------------------------------------------------------- 1 | // as seen on https://github.com/kangax/html-minifier/issues/63/#issuecomment-37772698 2 | 3 | const _isBooleanAttribute = attr => { 4 | return (/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|draggable|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|spellcheck|translate|truespeed|typemustmatch|visible)$/).test(attr); 5 | }; 6 | 7 | export default _isBooleanAttribute; 8 | -------------------------------------------------------------------------------- /dist/internal/dom/_isElementOrDocumentOrWindow.js: -------------------------------------------------------------------------------- 1 | import _isElement from "../helpers/_isElement.js"; 2 | 3 | const _isElementOrDocumentOrWindow = arg => _isElement(arg) || arg === document || arg === window; 4 | 5 | export default _isElementOrDocumentOrWindow; 6 | -------------------------------------------------------------------------------- /dist/internal/dom/_setAttrBase.js: -------------------------------------------------------------------------------- 1 | const _setAttrBase = (domEl, attrName, value) => { 2 | if (value != null && value !== "") { 3 | if (typeof value === "boolean") { 4 | if (value === false) { 5 | domEl.removeAttribute(attrName); 6 | } 7 | else { 8 | domEl.setAttribute(attrName, ""); 9 | } 10 | } 11 | else { 12 | domEl.setAttribute(attrName, value); 13 | } 14 | } 15 | }; 16 | 17 | export default _setAttrBase; 18 | -------------------------------------------------------------------------------- /dist/internal/event/_reactToEvent.js: -------------------------------------------------------------------------------- 1 | import _splitEventStr from "./_splitEventStr.js"; 2 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 3 | import _isString from "../helpers/_isString.js"; 4 | import _domElementsOrDocumentOrWindowToArray from "../dom/_domElementsOrDocumentOrWindowToArray.js"; 5 | 6 | const _reactToEvent = (methodName, domEls, eventStr, callback, useCapture) => { 7 | _throwErrorIf(!_isString(eventStr), "A string is expected as second parameter."); 8 | _splitEventStr(eventStr).forEach(eventType => _domElementsOrDocumentOrWindowToArray(domEls).forEach(domEl => domEl[methodName](eventType, callback, useCapture))); 9 | }; 10 | 11 | export default _reactToEvent; 12 | -------------------------------------------------------------------------------- /dist/internal/event/_splitEventStr.js: -------------------------------------------------------------------------------- 1 | const _splitEventStr = eventStr => eventStr.split(" "); 2 | 3 | export default _splitEventStr; 4 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getAxisEndProp.js: -------------------------------------------------------------------------------- 1 | const _getAxisEndProp = isHorizontal => isHorizontal ? "right" : "bottom"; 2 | 3 | export default _getAxisEndProp; 4 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getAxisInfo.js: -------------------------------------------------------------------------------- 1 | import _getAxisStartProp from "./_getAxisStartProp.js"; 2 | import _getAxisEndProp from "./_getAxisEndProp.js"; 3 | 4 | const _getAxisInfo = (domCoords, maskCoords, isHorizontal) => { 5 | let startProp = _getAxisStartProp(isHorizontal), 6 | endProp = _getAxisEndProp(isHorizontal); 7 | 8 | let domStart = domCoords[startProp], 9 | domEnd = domCoords[endProp], 10 | maskStart = maskCoords[startProp], 11 | maskEnd = maskCoords[endProp]; 12 | 13 | let startBeforeStart = domStart <= maskStart, 14 | startBeforeStartExc = domStart < maskStart, 15 | startAfterStart = domStart >= maskStart, 16 | startBeforeEnd = domStart <= maskEnd, 17 | startAfterEnd = domStart >= maskEnd; 18 | 19 | let endBeforeStart = domEnd <= maskStart, 20 | endAfterStart = domEnd >= maskStart, 21 | endBeforeEnd = domEnd <= maskEnd, 22 | endAfterEnd = domEnd >= maskEnd, 23 | endAfterEndExc = domEnd > maskEnd; 24 | 25 | let isOffBefore = startBeforeStart && endBeforeStart, 26 | isOffAfter = startAfterEnd && endAfterEnd, 27 | isContained = startAfterStart && endBeforeEnd, 28 | isWrapper = startBeforeStart && endAfterEnd, 29 | isClippedBefore = startBeforeStartExc && endAfterStart, 30 | isClippedAfter = startBeforeEnd && endAfterEndExc; 31 | 32 | return { 33 | isOffBefore, 34 | isOffAfter, 35 | isContained, 36 | isWrapper, 37 | isClippedBefore, 38 | isClippedAfter 39 | }; 40 | }; 41 | 42 | export default _getAxisInfo; 43 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getAxisStartProp.js: -------------------------------------------------------------------------------- 1 | const _getAxisStartProp = isHorizontal => isHorizontal ? "left" : "top"; 2 | 3 | export default _getAxisStartProp; 4 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getCoords.js: -------------------------------------------------------------------------------- 1 | import _isElement from "../helpers/_isElement.js"; 2 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 3 | 4 | const _getCoords = (maskDef, allowElementOnly) => { 5 | let maskCoords; 6 | 7 | if (_isElement(maskDef)) { 8 | maskCoords = maskDef.getBoundingClientRect(); 9 | } 10 | else if (maskDef != null && !allowElementOnly) { 11 | maskCoords = maskDef; 12 | } 13 | else if (!allowElementOnly) { 14 | maskCoords = { 15 | top: 0, 16 | bottom: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, 17 | left: 0, 18 | right: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth 19 | }; 20 | } 21 | 22 | if (!Number.isFinite(maskCoords.top) || !Number.isFinite(maskCoords.bottom) || !Number.isFinite(maskCoords.left) || !Number.isFinite(maskCoords.right)) { 23 | _throwErrorIf(allowElementOnly, `An HTMLElement is expected as parameter.`); 24 | _throwErrorIf(!allowElementOnly, `If passed, the optional parameter can be either an HTMLElement or an Object containing numeric values for "top", "bottom", "left" and "right" properties.`); 25 | } 26 | 27 | return maskCoords; 28 | }; 29 | 30 | export default _getCoords; 31 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getCoordsFromElement.js: -------------------------------------------------------------------------------- 1 | import _getCoords from "./_getCoords.js"; 2 | 3 | const _getCoordsFromElement = arg => _getCoords(arg, true); 4 | 5 | export default _getCoordsFromElement; 6 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getCoordsFromElementOrObjectOrWindow.js: -------------------------------------------------------------------------------- 1 | import _getCoords from "./_getCoords.js"; 2 | 3 | const _getCoordsFromElementOrObjectOrWindow = arg => _getCoords(arg, false); 4 | 5 | export default _getCoordsFromElementOrObjectOrWindow; 6 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getHorizontalAxisInfo.js: -------------------------------------------------------------------------------- 1 | import _getAxisInfo from "./_getAxisInfo.js"; 2 | 3 | const _getHorizontalAxisInfo = (domCoords, maskCoords) => _getAxisInfo(domCoords, maskCoords, true); 4 | 5 | export default _getHorizontalAxisInfo; 6 | -------------------------------------------------------------------------------- /dist/internal/geometry/_getVerticalAxisInfo.js: -------------------------------------------------------------------------------- 1 | import _getAxisInfo from "./_getAxisInfo.js"; 2 | 3 | const _getVerticalAxisInfo = (domCoords, maskCoords) => _getAxisInfo(domCoords, maskCoords, false); 4 | 5 | export default _getVerticalAxisInfo; -------------------------------------------------------------------------------- /dist/internal/helpers/_isBoolean.js: -------------------------------------------------------------------------------- 1 | const _isBoolean = arg => 2 | arg === true || arg === false; 3 | 4 | export default _isBoolean; 5 | -------------------------------------------------------------------------------- /dist/internal/helpers/_isElement.js: -------------------------------------------------------------------------------- 1 | const _isElement = arg => 2 | arg != null && typeof arg === "object" && arg.nodeType === Node.ELEMENT_NODE && 3 | typeof arg.style === "object" && typeof arg.ownerDocument === "object"; 4 | 5 | export default _isElement; 6 | -------------------------------------------------------------------------------- /dist/internal/helpers/_isPlainObject.js: -------------------------------------------------------------------------------- 1 | const _isPlainObject = obj => 2 | obj != null && typeof obj === "object" && 3 | (obj.constructor === Object || obj.constructor == null) && 4 | Object.prototype.toString.call(obj) === "[object Object]"; 5 | 6 | export default _isPlainObject; 7 | -------------------------------------------------------------------------------- /dist/internal/helpers/_isString.js: -------------------------------------------------------------------------------- 1 | const _isString = arg => 2 | typeof arg === 'string' || arg instanceof String; 3 | 4 | export default _isString; 5 | -------------------------------------------------------------------------------- /dist/internal/helpers/_parsePath.js: -------------------------------------------------------------------------------- 1 | const _parsePath = path => 2 | Array.isArray(path) ? path : `${path}`.split("."); 3 | 4 | export default _parsePath; 5 | -------------------------------------------------------------------------------- /dist/internal/helpers/_random.js: -------------------------------------------------------------------------------- 1 | const _random = (min, max) => 2 | Math.floor(Math.random() * (max - min + 1)) + min; 3 | 4 | export default _random; 5 | -------------------------------------------------------------------------------- /dist/internal/helpers/_simpleAt.js: -------------------------------------------------------------------------------- 1 | import _parsePath from "./_parsePath.js"; 2 | 3 | const _simpleAt = (obj, path) => 4 | _parsePath(path).reduce((obj, key) => { 5 | return (obj != null && obj.hasOwnProperty(key)) ? obj[key] : undefined; 6 | }, obj); 7 | 8 | export default _simpleAt; 9 | -------------------------------------------------------------------------------- /dist/internal/helpers/_simpleSet.js: -------------------------------------------------------------------------------- 1 | import _parsePath from "./_parsePath.js"; 2 | import _isPlainObject from "./_isPlainObject.js"; 3 | 4 | const _simpleSet = (obj, path, value) => 5 | _parsePath(path).reduce((obj, key, index, arr) => { 6 | let isLast = (index === arr.length - 1); 7 | 8 | if (!obj.hasOwnProperty(key) || (!isLast && !_isPlainObject(obj[key]))) { 9 | obj[key] = {}; 10 | } 11 | 12 | return (!isLast) ? obj[key] : obj[key] = value; 13 | }, obj); 14 | 15 | export default _simpleSet; 16 | -------------------------------------------------------------------------------- /dist/internal/sort/_customComparator.js: -------------------------------------------------------------------------------- 1 | import _defaultComparator from "./_defaultComparator.js"; 2 | 3 | const _customComparator = (primer, reverse) => { 4 | let result = _defaultComparator; 5 | 6 | if (primer) { 7 | result = (a, b) => _defaultComparator(primer(a), primer(b)); 8 | } 9 | 10 | if (reverse) { 11 | return (a, b) => -1 * result(a, b); 12 | } 13 | 14 | return result; 15 | }; 16 | 17 | export default _customComparator; 18 | -------------------------------------------------------------------------------- /dist/internal/sort/_defaultComparator.js: -------------------------------------------------------------------------------- 1 | import _isString from "../helpers/_isString.js"; 2 | 3 | const _defaultComparator = (a, b) => (_isString(a) && _isString(b)) ? 4 | a.localeCompare(b) : (a == b) ? 0 : (a < b) ? -1 : 1; 5 | 6 | export default _defaultComparator; 7 | -------------------------------------------------------------------------------- /dist/internal/sort/_parseSortField.js: -------------------------------------------------------------------------------- 1 | import _defaultComparator from "./_defaultComparator.js"; 2 | import _customComparator from "./_customComparator.js"; 3 | import _isString from "../helpers/_isString.js"; 4 | 5 | const _parseSortField = field => { 6 | let useDefault = _isString(field); 7 | 8 | return { 9 | path: (useDefault) ? field : field.path, 10 | comparator: (useDefault) ? _defaultComparator : _customComparator(field.primer, field.reverse) 11 | }; 12 | }; 13 | 14 | export default _parseSortField; 15 | -------------------------------------------------------------------------------- /dist/internal/sort/_parseSortFields.js: -------------------------------------------------------------------------------- 1 | import _parseSortField from "./_parseSortField.js"; 2 | 3 | const _parseSortFields = fields => fields.map(_parseSortField); 4 | 5 | export default _parseSortFields; 6 | -------------------------------------------------------------------------------- /dist/internal/sort/_recursiveSort.js: -------------------------------------------------------------------------------- 1 | import _simpleAt from "../helpers/_simpleAt.js"; 2 | 3 | const _recursiveSort = fields => (objA, objB) => { 4 | let field = fields[0], 5 | path = field.path, 6 | result = field.comparator(_simpleAt(objA, path), _simpleAt(objB, path)), 7 | nextFields = fields.slice(1); 8 | 9 | if (result !== 0 || nextFields.length === 0) { 10 | return result; 11 | } 12 | 13 | return _recursiveSort(nextFields)(objA, objB); 14 | }; 15 | 16 | export default _recursiveSort; 17 | -------------------------------------------------------------------------------- /dist/internal/string/_sliceOnOccurrence.js: -------------------------------------------------------------------------------- 1 | import _isString from "../helpers/_isString.js"; 2 | import _throwErrorIf from "../validation/_throwErrorIf.js"; 3 | 4 | const _sliceOnOccurrence = (type, position, str, delimiter) => { 5 | _throwErrorIf(!_isString(str), "A string is expected as the first parameter."); 6 | 7 | let index = (position === "first") ? str.indexOf(delimiter) : str.lastIndexOf(delimiter); 8 | 9 | if (index === -1) { 10 | return undefined; 11 | } 12 | 13 | return (type === "before") ? str.substr(0, index) : str.substr(index + delimiter.length); 14 | }; 15 | 16 | export default _sliceOnOccurrence; 17 | -------------------------------------------------------------------------------- /dist/internal/validation/_throwErrorIf.js: -------------------------------------------------------------------------------- 1 | const _throwErrorIf = (condition, error) => { 2 | if (condition) { 3 | throw new Error(error); 4 | } 5 | }; 6 | 7 | export default _throwErrorIf; 8 | -------------------------------------------------------------------------------- /dist/listen.js: -------------------------------------------------------------------------------- 1 | import _reactToEvent from "./internal/event/_reactToEvent.js"; 2 | 3 | /** 4 | * Adds one or more event listeners to one or more DOM elements at once. 5 | * The function is a wrapper for 6 | * [`Element.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) 7 | * that accepts a space-separated event names string and a group 8 | * of target DOM elements. 9 | * 10 | * @category Event 11 | * @param {(Window|HTMLDocument|HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements, including `document` and `window`. 12 | * @param {string} eventStr The event names string. 13 | * @param {Function} callback The function to be exectuted when the event is dispatched. 14 | * @param {Boolean} [useCapture = false] The event phase to be listened for. 15 | * 16 | * @example 17 | * 18 | * let oneElement = document.querySelector("a"); 19 | * let manyElements = document.querySelectorAll("a"); 20 | * 21 | * listen(oneElement, "click", reactToClick, true); 22 | * listen(manyElements, "click", reactToClick); 23 | * listen(window, "load resize", reactToLoadAndResize); 24 | */ 25 | const listen = (domEls, eventStr, callback, useCapture = false) => _reactToEvent("addEventListener", domEls, eventStr, callback, useCapture); 26 | 27 | export default listen; 28 | -------------------------------------------------------------------------------- /dist/modifyBemClass.js: -------------------------------------------------------------------------------- 1 | import _validateModifyBemClassArgs from "./internal/bem/_validateModifyBemClassArgs.js"; 2 | import _parseBem from "./internal/bem/_parseBem.js"; 3 | 4 | /** 5 | * @typedef {Object} BemObject 6 | * @property {Object} name An object which key is a BEM block or element, e.g. `menu` or `landing__area`. 7 | * @property {Object} name.modifier An object representing a modifiers and their values, e.g. `{active: false}` or `{level: 42}`. 8 | */ 9 | 10 | /** 11 | * Modifies the CSS classes from a DOM element according 12 | * to the [BEM methodology](https://en.bem.info/methodology/). 13 | * 14 | * @category BEM 15 | * 16 | * @param {HTMLElement} domEl The DOM element. 17 | * @param {BemObject} bemObj The object describing BEM class changes (see table below). 18 | * @param {Array} delimiters The BEM delimiters. 19 | * 20 | * @example 21 | * let domEl = document.createElement("div"), 22 | * delimiters = ["__", "--", "-"]; 23 | * 24 | * modifyBemClass(domEl, { 25 | * "swiper": { 26 | * "slides": 5, 27 | * "current": 2, 28 | * "playing": true 29 | * } 30 | * }, delimiters); 31 | * 32 | * domEl.className; 33 | * // => "swiper swiper--slides-5 swiper--current-2 swiper--playing" 34 | * 35 | * modifyBemClass(domEl, { 36 | * "swiper": { 37 | * "current": 3, 38 | * "playing": false 39 | * } 40 | * }, delimiters); 41 | * 42 | * domEl.className; 43 | * // => "swiper swiper--slides-5 swiper--current-3" 44 | */ 45 | const modifyBemClass = (domEl, bemObj, delimiters) => { 46 | _validateModifyBemClassArgs(domEl, bemObj, delimiters); 47 | return _parseBem(bemObj, domEl, delimiters, true); 48 | }; 49 | 50 | export default modifyBemClass; 51 | -------------------------------------------------------------------------------- /dist/modifyBemClassCompact.js: -------------------------------------------------------------------------------- 1 | import _validateModifyBemClassArgs from "./internal/bem/_validateModifyBemClassArgs.js"; 2 | import _parseBem from "./internal/bem/_parseBem.js"; 3 | 4 | /** 5 | * Modifies the CSS classes from a DOM element according 6 | * to the [BEM methodology](https://en.bem.info/methodology/). 7 | * Unlike [`modifyBemClass()`](#modifybemclass), it ommits the original block 8 | * or element CSS class if a modified version is output. 9 | * 10 | * @category BEM 11 | * 12 | * @param {HTMLElement} domEl The DOM element. 13 | * @param {BemObject} bemObj The object describing BEM class changes (see table below). 14 | * @param {Array} delimiters The BEM delimiters. 15 | * 16 | * @example 17 | * let domEl = document.createElement("div"), 18 | * delimiters = ["__", "--", "-"]; 19 | * 20 | * modifyBemClassCompact(domEl, { 21 | * "swiper": { 22 | * "slides": 5, 23 | * "current": 2, 24 | * "playing": true 25 | * } 26 | * }, delimiters); 27 | * 28 | * domEl.className; 29 | * // => "swiper--slides-5 swiper--current-2 swiper--playing" 30 | * 31 | * modifyBemClassCompact(domEl, { 32 | * "swiper": { 33 | * "current": 3, 34 | * "playing": false 35 | * } 36 | * }, delimiters); 37 | * 38 | * domEl.className; 39 | * // => "swiper--slides-5 swiper--current-3" 40 | */ 41 | const modifyBemClassCompact = (domEl, bemObj, delimiters) => { 42 | _validateModifyBemClassArgs(domEl, bemObj, delimiters); 43 | return _parseBem(bemObj, domEl, delimiters, false); 44 | }; 45 | 46 | export default modifyBemClassCompact; 47 | -------------------------------------------------------------------------------- /dist/oneOutOf.js: -------------------------------------------------------------------------------- 1 | import _random from "./internal/helpers/_random.js"; 2 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 3 | 4 | /** 5 | * Returns `true` approximately one out of `num` times, 6 | * randomly. 7 | * 8 | * @category Random 9 | * 10 | * @param {number} num A number greater than zero. 11 | * @return {boolean} Returns `true` approximately one out of `num` times. 12 | * 13 | * @example 14 | * oneOutOf(2); 15 | * // => true 16 | * 17 | * oneOutOf(2); 18 | * // => false 19 | */ 20 | const oneOutOf = num => { 21 | _throwErrorIf((!Number.isFinite(num) || num < 1), "A number greater than 1 is expected as parameter."); 22 | return (num === 1) ? true : _random(1, num) === num; 23 | }; 24 | 25 | export default oneOutOf; 26 | -------------------------------------------------------------------------------- /dist/parents.js: -------------------------------------------------------------------------------- 1 | import _isElementOrDocumentOrWindow from "./internal/dom/_isElementOrDocumentOrWindow.js"; 2 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 3 | 4 | /** 5 | * Returns all parents of a DOM element, 6 | * from the closest to the most distant. 7 | * 8 | * @category DOM 9 | * @param {HTMLElement} domEl The DOM element. 10 | * @return {Array.} The DOM element parents. 11 | * 12 | * @example 13 | * let domChild = document.createElement("div"), 14 | * domParent = document.createElement("div"), 15 | * domGrandparent = document.createElement("div"), 16 | * body = document.body, 17 | * html = document.querySelector("html"); 18 | * 19 | * domParent.appendChild(domChild); 20 | * domGrandparent.appendChild(domParent); 21 | * body.appendChild(domGrandparent); 22 | * 23 | * parents(domChild); 24 | * // => [domParent, domGrandparent, body, html, document] 25 | */ 26 | const parents = (domEl, memo = []) => { 27 | if (memo.length === 0) { 28 | _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement is expected as parameter."); 29 | } 30 | 31 | let parentNode = domEl.parentNode; 32 | return (!parentNode) ? memo : parents(parentNode, memo.concat([parentNode])); 33 | }; 34 | 35 | export default parents; 36 | -------------------------------------------------------------------------------- /dist/removeAttr.js: -------------------------------------------------------------------------------- 1 | import _setAttrBase from "./internal/dom/_setAttrBase.js"; 2 | import _domElementsToArray from "./internal/dom/_domElementsToArray.js"; 3 | 4 | /** 5 | * Removes an attribute from one or more DOM elements using native 6 | * [`Element.removeAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute). 7 | * 8 | * @category DOM 9 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 10 | * @param {string} attrName The attribute name. 11 | * 12 | * @example 13 | * let oneElement = createDomElement('Level 42'); 14 | * 15 | * removeAttr(oneElement, "data-level"); 16 | * 17 | * oneElement.getAttribute("data-level"); 18 | * // => null 19 | * 20 | * oneElement.dataset.level; 21 | * // => undefined 22 | * 23 | * @example 24 | * let oneElement = createDomElement('News'); 25 | * 26 | * removeAttr(oneElement, "class"); 27 | * 28 | * oneElement.getAttribute("class"); 29 | * // => null 30 | * 31 | * oneElement.className; 32 | * // => "" 33 | * 34 | * @example 35 | * let listHtml = 'AB'; 36 | * listElement = createDomElement(listElement), 37 | * manyElements = listElement.querySelectorAll("li"); 38 | * 39 | * removeAttr(manyElements, "class"); 40 | * 41 | * manyElements[0].className; 42 | * // => "" 43 | * 44 | * manyElements[1].className; 45 | * // => "" 46 | */ 47 | const removeAttr = (domEls, attrName) => { 48 | _domElementsToArray(domEls).forEach(domEl => _setAttrBase(domEl, attrName, false)); 49 | }; 50 | 51 | export default removeAttr; 52 | -------------------------------------------------------------------------------- /dist/removeAttrs.js: -------------------------------------------------------------------------------- 1 | import removeAttr from "./removeAttr.js"; 2 | 3 | /** 4 | * The same as [`removeAttr()`](#removeattr), except it takes an 5 | * array with attributes to be removed. 6 | * 7 | * @category DOM 8 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 9 | * @param {Array.} attrArr The array with attributes to be removed. 10 | * 11 | * @example 12 | * let oneElement = createDomElement('Level 42'); 13 | * 14 | * removeAttrs(oneElement, ["data-level", "class"]); 15 | * 16 | * oneElement.getAttribute("data-level"); 17 | * // => null 18 | * 19 | * oneElement.dataset.level; 20 | * // => undefined 21 | * 22 | * oneElement.getAttribute("class"); 23 | * // => null 24 | * 25 | * oneElement.className; 26 | * // => "" 27 | * 28 | * @example 29 | * let listHtml = 'AB', 30 | * listElement = createDomElement(listHtml), 31 | * manyElements = listElement.querySelectorAll("li"); 32 | * 33 | * removeAttrs(manyElements, ["data-level", "class"]); 34 | * 35 | * manyElements[0].getAttribute("data-level"); 36 | * // => null 37 | * 38 | * manyElements[1].getAttribute("data-level"); 39 | * // => null 40 | * 41 | * manyElements[0].className; 42 | * // => "" 43 | * 44 | * manyElements[1].className; 45 | * // => "" 46 | */ 47 | const removeAttrs = (domEls, attrArr) => { 48 | attrArr.forEach(attrName => removeAttr(domEls, attrName)); 49 | }; 50 | 51 | export default removeAttrs; 52 | -------------------------------------------------------------------------------- /dist/removeClass.js: -------------------------------------------------------------------------------- 1 | import _changeClassWithMethod from "./internal/classname/_changeClassWithMethod.js"; 2 | import _domElementsToArray from "./internal/dom/_domElementsToArray.js"; 3 | 4 | /** 5 | * Removes a CSS class from one or more DOM elements. 6 | * 7 | * @category ClassName 8 | * 9 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 10 | * @param {string} className The CSS class name. 11 | * 12 | * @example 13 | * let oneElement = document.querySelector(".link.base"); 14 | * removeClass(oneElement, "link"); 15 | * 16 | * oneElement.className; 17 | * // => "base" 18 | * 19 | * @example 20 | * let manyElements = document.querySelectorAll(".link.base"); 21 | * removeClass(manyElements, "link"); 22 | * 23 | * manyElements[0].className; 24 | * // => "base" 25 | */ 26 | const removeClass = (domEls, className) => { 27 | _domElementsToArray(domEls).forEach(domEl => _changeClassWithMethod(domEl, className, "remove")); 28 | }; 29 | 30 | export default removeClass; 31 | -------------------------------------------------------------------------------- /dist/selfAndParents.js: -------------------------------------------------------------------------------- 1 | import parents from "./parents.js"; 2 | import _isElementOrDocumentOrWindow from "./internal/dom/_isElementOrDocumentOrWindow.js"; 3 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 4 | 5 | /** 6 | * The same as [`parents()`](#parents), except it includes 7 | * the DOM element itself. 8 | * 9 | * @category DOM 10 | * @param {HTMLElement} domEl The DOM element. 11 | * @return {Array.} The DOM element and its parents. 12 | * 13 | * @example 14 | * let domChild = document.createElement("div"), 15 | * domParent = document.createElement("div"), 16 | * domGrandparent = document.createElement("div"), 17 | * body = document.body, 18 | * html = document.querySelector("html"); 19 | * 20 | * domParent.appendChild(domChild); 21 | * domGrandparent.appendChild(domParent); 22 | * body.appendChild(domGrandparent); 23 | * 24 | * selfAndParents(domChild); 25 | * // => [domChild, domParent, domGrandparent, body, html, document] 26 | */ 27 | const selfAndParents = domEl => { 28 | _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement is expected as parameter."); 29 | return [domEl].concat(parents(domEl)); 30 | }; 31 | 32 | export default selfAndParents; 33 | -------------------------------------------------------------------------------- /dist/setAttr.js: -------------------------------------------------------------------------------- 1 | import _setAttrBase from "./internal/dom/_setAttrBase.js"; 2 | import _domElementsToArray from "./internal/dom/_domElementsToArray.js"; 3 | 4 | /** 5 | * Sets an attribute for one or more DOM elements using native 6 | * [`Element.setAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute). 7 | * 8 | * @category DOM 9 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 10 | * @param {string} attrName The attribute name. 11 | * @param {(string|number|boolean)} value The attribute value. 12 | * 13 | * @example 14 | * let oneElement = createDomElement('Level 42'); 15 | * 16 | * setAttr(oneElement, "data-level", 42); 17 | * 18 | * oneElement.getAttribute("data-level"); 19 | * // => "42" 20 | * 21 | * oneElement.dataset.level; 22 | * // => "42" 23 | * 24 | * @example 25 | * let oneElement = createDomElement('News'); 26 | * 27 | * setAttr(oneElement, "class", "button"); 28 | * 29 | * oneElement.getAttribute("class"); 30 | * // => "button" 31 | * 32 | * oneElement.className; 33 | * // => "button" 34 | * 35 | * @example 36 | * let listElement = createDomElement('ABC'), 37 | * manyElements = listElement.querySelectorAll("li"); 38 | * 39 | * setAttr(manyElements, "class", "item"); 40 | * 41 | * manyElements[0].className; 42 | * // => "item" 43 | * 44 | * manyElements[1].className; 45 | * // => "item" 46 | */ 47 | const setAttr = (domEls, attrName, value) => { 48 | _domElementsToArray(domEls).forEach(domEl => _setAttrBase(domEl, attrName, value)); 49 | }; 50 | 51 | export default setAttr; 52 | -------------------------------------------------------------------------------- /dist/setAttrs.js: -------------------------------------------------------------------------------- 1 | import setAttr from "./setAttr.js"; 2 | 3 | /** 4 | * The same as [`setAttr()`](#setattr), except it takes an object 5 | * with attribute name and value pairs to set one or 6 | * many attributes at once. 7 | * 8 | * @category DOM 9 | * @param {(HTMLElement|HTMLCollection|NodeList|Array.|Set.)} domEls One or more DOM elements. 10 | * @param {Object} attrObj The object with attribute name and value pairs, e.g. `{ "data-level": 42 }`. 11 | * 12 | * @example 13 | * let oneElement = createDomElement('Level 42'); 14 | * 15 | * setAttrs(oneElement, { 16 | * "data-level": 42, 17 | * "class": "level" 18 | * }); 19 | * 20 | * oneElement.getAttribute("data-level"); 21 | * // => "42" 22 | * 23 | * oneElement.dataset.level; 24 | * // => "42" 25 | * 26 | * oneElement.getAttribute("class"); 27 | * // => "level" 28 | * 29 | * oneElement.className; 30 | * // => "level" 31 | * 32 | * @example 33 | * let listElement = createDomElement('ABC'), 34 | * manyElements = listElement.querySelectorAll("li"); 35 | * 36 | * setAttrs(manyElements, { 37 | * "data-level": 42, 38 | * "class": "item" 39 | * }); 40 | * 41 | * manyElements[0].getAttribute("data-level"); 42 | * // => "42" 43 | * 44 | * manyElements[1].getAttribute("data-level"); 45 | * // => "42" 46 | * 47 | * manyElements[0].className; 48 | * // => "item" 49 | * 50 | * manyElements[1].className; 51 | * // => "item" 52 | */ 53 | const setAttrs = (domEls, attrObj) => { 54 | Object.keys(attrObj).forEach(attrKey => setAttr(domEls, attrKey, attrObj[attrKey])); 55 | }; 56 | 57 | export default setAttrs; 58 | -------------------------------------------------------------------------------- /dist/timeSince.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the time passed since a timestamp, in milliseconds; 3 | * 4 | * @category Time 5 | * @param {number} timestamp The time stamp. 6 | * @return {number} Time passed since the timestamp, in milliseconds. 7 | * 8 | * @example 9 | * let timestamp = +new Date(), 10 | * result = 0; 11 | * 12 | * setTimeout(() => { 13 | * result = timeSince(timestamp); 14 | * }, 150); 15 | * 16 | * result; 17 | * // => 150 18 | * // This value is approximate and may vary. 19 | */ 20 | const timeSince = timestamp => { 21 | return Number.isFinite(timestamp) ? ((+new Date()) - timestamp) : undefined; 22 | }; 23 | 24 | export default timeSince; 25 | -------------------------------------------------------------------------------- /dist/toAverage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * When used with `[].reduce()`, returns 3 | * the average of the values in an array. 4 | * 5 | * Note that reducing arrays with non-numeric values 6 | * using `toAverage()` can lead to unexpected results. 7 | * Also, note that the parentheses can be ommited. 8 | * 9 | * @category Reduce 10 | * 11 | * @return {number} The average of the values in an array. 12 | * @public 13 | * 14 | * @example 15 | * [3, 5, 7, 9].reduce(toAverage()); 16 | * // => 6 17 | * 18 | * [3, 5, 7, 9].reduce(toAverage); 19 | * // => 6 20 | */ 21 | const toAverage = (...args) => { 22 | let func = (prevNum, nextNum, index, arr) => { 23 | let isLastIteration = (index === arr.length - 1); 24 | return (!isLastIteration) ? (prevNum + nextNum) : (prevNum + nextNum) / arr.length; 25 | }; 26 | 27 | return (args.length === 0) ? func : func(...args); 28 | }; 29 | 30 | export default toAverage; 31 | -------------------------------------------------------------------------------- /dist/toAverageProp.js: -------------------------------------------------------------------------------- 1 | import toAverage from "./toAverage.js"; 2 | import _simpleAt from "./internal/helpers/_simpleAt.js"; 3 | 4 | /** 5 | * When used with `[].reduce()`, returns 6 | * the object in an array in which a specific property, 7 | * passed as parameter, has the closest value to the average. 8 | * 9 | * If two or more results are found, the first one 10 | * is returned. 11 | * 12 | * Note that the function expects the reduced array to be 13 | * formed by objects with the same set of properties. 14 | * 15 | * @category Reduce 16 | * 17 | * @param {string} path The path to the property of an object. 18 | * @return {Object} The object in which an specific property has the closest value to the average. 19 | * @public 20 | * 21 | * @example 22 | * let cities = [{ 23 | * "city": "Rio de Janeiro", 24 | * "temperature": 96, 25 | * "demographics": { 26 | * "population": 6.32 27 | * } 28 | * }, { 29 | * "city": "São Paulo", 30 | * "temperature": 82.5, 31 | * "demographics": { 32 | * "population": 12.04 33 | * } 34 | * }, { 35 | * "city": "Curitiba", 36 | * "temperature": 70, 37 | * "demographics": { 38 | * "population": 1.752 39 | * } 40 | * }, { 41 | * "city": "Florianópolis", 42 | * "temperature": 86, 43 | * "demographics": { 44 | * "population": 0.249 45 | * } 46 | * }]; 47 | * 48 | * // average "temperature": 83.625 49 | * // average "population": 5.09025 50 | * 51 | * cities.reduce(toAverageProp("temperature")); 52 | * // => { "city": "São Paulo", [...] } 53 | * 54 | * cities.reduce(toAverageProp("demographics.population")); 55 | * // => { "city": "Rio de Janeiro", [...] } 56 | */ 57 | const toAverageProp = path => { 58 | let average; 59 | 60 | return (prevObj, nextObj, index, arr) => { 61 | average = average || arr.map(obj => _simpleAt(obj, path)).reduce(toAverage()); 62 | 63 | if (Math.abs(_simpleAt(prevObj, path) - average) <= Math.abs(_simpleAt(nextObj, path) - average)) { 64 | return prevObj; 65 | } 66 | else { 67 | return nextObj; 68 | } 69 | }; 70 | }; 71 | 72 | export default toAverageProp; 73 | -------------------------------------------------------------------------------- /dist/toClosest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * When used with `[].reduce()`, returns 3 | * the closest value to the one passed as parameter. 4 | * 5 | * If two or more results are found, the first one 6 | * is returned. 7 | * 8 | * Note that reducing arrays with non-numeric values 9 | * using `toClosest()` can lead to unexpected results. 10 | * 11 | * @category Reduce 12 | * 13 | * @param {number} num The base value. 14 | * @return {number} The value, from an array, closest to the base value. 15 | * @public 16 | * 17 | * @example 18 | * [3, 5, 7, 9].reduce(toClosest(6)); 19 | * // => 5 20 | * 21 | * [3, 5, 7, 9].reduce(toClosest(-2)); 22 | * // => 3 23 | */ 24 | const toClosest = num => (prevNum, nextNum) => { 25 | if (Math.abs(prevNum - num) <= Math.abs(nextNum - num)) { 26 | return prevNum; 27 | } 28 | else { 29 | return nextNum; 30 | } 31 | }; 32 | 33 | export default toClosest; 34 | -------------------------------------------------------------------------------- /dist/toClosestProp.js: -------------------------------------------------------------------------------- 1 | import _simpleAt from "./internal/helpers/_simpleAt.js"; 2 | 3 | /** 4 | * When used with `[].reduce()`, returns 5 | * the object in an array in which a specific property, 6 | * passed as parameter, has the closest value to another, 7 | * also passed as parameter. 8 | * 9 | * If two or more results are found, the first one 10 | * is returned. 11 | * 12 | * Note that the function expects the reduced array to be 13 | * formed by objects with the same set of properties. 14 | * 15 | * @category Reduce 16 | * 17 | * @param {string} path The path to the property of an object. 18 | * @param {string} num The base value. 19 | * @return {Object} The object in which an specific property has the closest value to the base value. 20 | * @public 21 | * 22 | * @example 23 | * let cities = [{ 24 | * "city": "Rio de Janeiro", 25 | * "temperature": 96, 26 | * "demographics": { 27 | * "population": 6.32 28 | * } 29 | * }, { 30 | * "city": "São Paulo", 31 | * "temperature": 82.5, 32 | * "demographics": { 33 | * "population": 12.04 34 | * } 35 | * }, { 36 | * "city": "Curitiba", 37 | * "temperature": 70, 38 | * "demographics": { 39 | * "population": 1.752 40 | * } 41 | * }, { 42 | * "city": "Florianópolis", 43 | * "temperature": 86, 44 | * "demographics": { 45 | * "population": 0.249 46 | * } 47 | * }]; 48 | * 49 | * cities.reduce(toClosestProp("temperature", 75)); 50 | * // => { "city": "Curitiba", [...] } 51 | * 52 | * cities.reduce(toClosestProp("demographics.population", 5)); 53 | * // => { "city": "Rio de Janeiro", [...] } 54 | */ 55 | const toClosestProp = (path, num) => (prevObj, nextObj, index, arr) => { 56 | if (Math.abs(_simpleAt(prevObj, path) - num) <= Math.abs(_simpleAt(nextObj, path) - num)) { 57 | return prevObj; 58 | } 59 | 60 | return nextObj; 61 | }; 62 | 63 | export default toClosestProp; 64 | -------------------------------------------------------------------------------- /dist/toLargestProp.js: -------------------------------------------------------------------------------- 1 | import _simpleAt from "./internal/helpers/_simpleAt.js"; 2 | 3 | /** 4 | * When used with `[].reduce()`, returns 5 | * the object in an array in which a specific property 6 | * has the largest value. 7 | * 8 | * If two or more results are found, the first one 9 | * is returned. 10 | * 11 | * Note that the function expects the reduced array to be 12 | * formed by objects with the same set of properties. 13 | * 14 | * @category Reduce 15 | * 16 | * @param {string} path The path to the property of an object. 17 | * @return {Object} The object in which a specific property has the largest value. 18 | * @public 19 | * 20 | * @example 21 | * let cities = [{ 22 | * "city": "Rio de Janeiro", 23 | * "temperature": 96, 24 | * "demographics": { 25 | * "population": 6.32 26 | * } 27 | * }, { 28 | * "city": "São Paulo", 29 | * "temperature": 82.5, 30 | * "demographics": { 31 | * "population": 12.04 32 | * } 33 | * }, { 34 | * "city": "Curitiba", 35 | * "temperature": 70, 36 | * "demographics": { 37 | * "population": 1.752 38 | * } 39 | * }, { 40 | * "city": "Florianópolis", 41 | * "temperature": 86, 42 | * "demographics": { 43 | * "population": 0.249 44 | * } 45 | * }]; 46 | * 47 | * cities.reduce(toLargestProp("temperature")); 48 | * // => { "city": "Rio de Janeiro", [...] } 49 | * 50 | * cities.reduce(toLargestProp("demographics.population")); 51 | * // => { "city": "São Paulo", [...] } 52 | */ 53 | const toLargestProp = path => (prevObj, nextObj) => { 54 | if (_simpleAt(prevObj, path) >= _simpleAt(nextObj, path)) { 55 | return prevObj; 56 | } 57 | 58 | return nextObj; 59 | }; 60 | 61 | export default toLargestProp; 62 | -------------------------------------------------------------------------------- /dist/toSmallestProp.js: -------------------------------------------------------------------------------- 1 | import _simpleAt from "./internal/helpers/_simpleAt.js"; 2 | 3 | /** 4 | * When used with `[].reduce()`, returns 5 | * the object in an array in which a specific property 6 | * has the smallest property. 7 | * 8 | * If two or more results are found, the first one 9 | * is returned. 10 | * 11 | * Note that the function expects the reduced array to be 12 | * formed by objects with the same set of properties. 13 | * 14 | * @category Reduce 15 | * 16 | * @param {string} path The path to the property of an object. 17 | * @return {Object} The object in which a specific property has the smallest value. 18 | * @public 19 | * 20 | * @example 21 | * let cities = [{ 22 | * "city": "Rio de Janeiro", 23 | * "temperature": 96, 24 | * "demographics": { 25 | * "population": 6.32 26 | * } 27 | * }, { 28 | * "city": "São Paulo", 29 | * "temperature": 82.5, 30 | * "demographics": { 31 | * "population": 12.04 32 | * } 33 | * }, { 34 | * "city": "Curitiba", 35 | * "temperature": 70, 36 | * "demographics": { 37 | * "population": 1.752 38 | * } 39 | * }, { 40 | * "city": "Florianópolis", 41 | * "temperature": 86, 42 | * "demographics": { 43 | * "population": 0.249 44 | * } 45 | * }]; 46 | * 47 | * cities.reduce(toSmallestProp("temperature")); 48 | * // => { "city": "Curitiba", [...] } 49 | * 50 | * cities.reduce(toSmallestProp("demographics.population")); 51 | * // => { "city": "Florianópolis", [...] } 52 | */ 53 | const toSmallestProp = path => (prevObj, nextObj) => { 54 | if (_simpleAt(nextObj, path) >= _simpleAt(prevObj, path)) { 55 | return prevObj; 56 | } 57 | 58 | return nextObj; 59 | }; 60 | 61 | export default toSmallestProp; 62 | -------------------------------------------------------------------------------- /dist/toSum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * When used with `[].reduce()`, returns 3 | * the sum of the values in an array. 4 | * 5 | * Note that reducing arrays with non-numeric values 6 | * using `toSum()` can lead to unexpected results. 7 | * Also, note that the parentheses can be ommited. 8 | * 9 | * @category Reduce 10 | * 11 | * @return {number} The sum of the values in an array. 12 | * @public 13 | * 14 | * @example 15 | * [3, 5, 7, 9].reduce(toSum()); 16 | * // => 24 17 | * 18 | * [3, 5, 7, 9].reduce(toSum); 19 | * // => 24 20 | */ 21 | const toSum = (...args) => { 22 | let func = (prevNum, nextNum) => prevNum + nextNum; 23 | return (args.length === 0) ? func : func(...args); 24 | }; 25 | 26 | export default toSum; 27 | -------------------------------------------------------------------------------- /dist/trigger.js: -------------------------------------------------------------------------------- 1 | import _isString from "./internal/helpers/_isString.js"; 2 | import _isElementOrDocumentOrWindow from "./internal/dom/_isElementOrDocumentOrWindow.js"; 3 | import _throwErrorIf from "./internal/validation/_throwErrorIf.js"; 4 | 5 | /** 6 | * Triggers a custom DOM event. 7 | * 8 | * @category Event 9 | * @param {(Window|HTMLDocument|HTMLElement)} domEl The DOM element, including `document` and `window`. 10 | * @param {string} evtName The event name. 11 | * @param {boolean} [bubbles = false] Whether the event bubbles. 12 | * @param {boolean} [cancelable = false] Whether the event can be canceled. 13 | * @param {*} [detail] Any information passed along. 14 | * 15 | * @example 16 | * let popupButton = document.querySelector(".popup__button"), 17 | * popupLayer = document.querySelector(".popup__layer"); 18 | * 19 | * popupButton.addEventListener("click", evt => { 20 | * trigger(popupLayer, "open"); 21 | * }); 22 | */ 23 | const trigger = (domEl, evtName, bubbles = false, cancelable = false, detail = undefined) => { 24 | _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement, including document or window, is expected as first parameter."); 25 | _throwErrorIf(!_isString(evtName), "A string is expected as second parameter."); 26 | 27 | let evt; 28 | 29 | if (window.CustomEvent != null) { 30 | evt = document.createEvent("CustomEvent"); 31 | evt.initCustomEvent(evtName, bubbles, cancelable, detail); 32 | } 33 | else { 34 | evt = new CustomEvent(evtName, { 35 | bubbles, 36 | cancelable, 37 | detail 38 | }); 39 | } 40 | 41 | domEl.dispatchEvent(evt); 42 | }; 43 | 44 | export default trigger; 45 | -------------------------------------------------------------------------------- /dist/waitInPromise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Delays the chaining of a promise by a specified 3 | * time in milliseconds. 4 | * 5 | * The function is curried so as to be used inside 6 | * the `.then()` method, passing along the resolved 7 | * value from the previous promise step to the next. 8 | * 9 | * Note that if a non-numeric parameter is passed, 10 | * the promise resolves without delay, skipping the 11 | * internal `setTimeout()`. 12 | * 13 | * @category Promise 14 | * 15 | * @param {number} delay The delay in milliseconds. 16 | * @return {Promise} When fulfilled, returns the resolved value from the previous step. 17 | * @public 18 | * 19 | * @example 20 | * Promise.resolve("waiting") 21 | * .then(waitInPromise(1000)) 22 | * .then(doSomethingAfterOneSecond); 23 | */ 24 | const waitInPromise = delay => arg => 25 | (Number.isFinite(delay) && delay > 0) ? 26 | new Promise(resolve => setTimeout(() => resolve(arg), delay)) : 27 | Promise.resolve(arg); 28 | 29 | export default waitInPromise; 30 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | canivete.leofavre.com -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | ruby RUBY_VERSION 3 | 4 | gem "jekyll", "3.4.0" 5 | gem "jekyll-autoprefixer" -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.5.1) 5 | public_suffix (~> 2.0, >= 2.0.2) 6 | autoprefixer-rails (6.3.7) 7 | execjs 8 | colorator (1.1.0) 9 | execjs (2.7.0) 10 | ffi (1.9.18) 11 | forwardable-extended (2.6.0) 12 | jekyll (3.4.0) 13 | addressable (~> 2.4) 14 | colorator (~> 1.0) 15 | jekyll-sass-converter (~> 1.0) 16 | jekyll-watch (~> 1.1) 17 | kramdown (~> 1.3) 18 | liquid (~> 3.0) 19 | mercenary (~> 0.3.3) 20 | pathutil (~> 0.9) 21 | rouge (~> 1.7) 22 | safe_yaml (~> 1.0) 23 | jekyll-autoprefixer (1.0.1) 24 | autoprefixer-rails (~> 6.3.6) 25 | jekyll-sass-converter (1.5.0) 26 | sass (~> 3.4) 27 | jekyll-watch (1.5.0) 28 | listen (~> 3.0, < 3.1) 29 | kramdown (1.13.2) 30 | liquid (3.0.6) 31 | listen (3.0.8) 32 | rb-fsevent (~> 0.9, >= 0.9.4) 33 | rb-inotify (~> 0.9, >= 0.9.7) 34 | mercenary (0.3.6) 35 | pathutil (0.14.0) 36 | forwardable-extended (~> 2.6) 37 | public_suffix (2.0.5) 38 | rb-fsevent (0.9.8) 39 | rb-inotify (0.9.8) 40 | ffi (>= 0.5.0) 41 | rouge (1.11.1) 42 | safe_yaml (1.0.4) 43 | sass (3.4.23) 44 | 45 | PLATFORMS 46 | ruby 47 | 48 | DEPENDENCIES 49 | jekyll (= 3.4.0) 50 | jekyll-autoprefixer 51 | 52 | RUBY VERSION 53 | ruby 2.4.0p0 54 | 55 | BUNDLED WITH 56 | 1.14.6 57 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: Canivete 2 | email: leo@leofavre.com 3 | description: > 4 | Canivete (portuguese for army knife) is a set of Javascript 5 | multi-purpose functions; a personal and on-going project 6 | where I keep functions that I find myself writing over and 7 | over again. 8 | baseurl: "/canivete" 9 | url: "https://leofavre.github.io" 10 | github_username: leofavre 11 | 12 | # Build settings 13 | markdown: kramdown 14 | 15 | exclude: 16 | - Gemfile 17 | - Gemfile.lock 18 | - index.js 19 | 20 | gems: 21 | - jekyll-autoprefixer 22 | 23 | autoprefixer: 24 | browsers: 25 | - last 4 versions -------------------------------------------------------------------------------- /docs/_ejs/content.ejs: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Canivete 4 | --- 5 | 6 | <%if(docs) docs.forEach(category => {-%> 7 | ## *<%-category.name%>* 8 | 9 | ><%if(category.items) category.items.forEach((item, index, arr) => {-%> 10 | [<%-item.name%>](#<%-item.href%>)<%if(index < arr.length - 2){-%>, <%}-%><%if(index === arr.length - 2){-%> & <%}-%> 11 | <%})%> 12 | 13 | <%-include contentLoop.ejs-%> 14 | <%})-%> -------------------------------------------------------------------------------- /docs/_ejs/contentItem.ejs: -------------------------------------------------------------------------------- 1 | ### `<%-item.signature%>` 2 | 3 | <%-item.description%> 4 | <%-include itemParameters.ejs-%> 5 | <%-include itemTypeDefParams.ejs-%> 6 | <%-include itemReturns.ejs-%> 7 | <%-include itemTypeDefReturns.ejs-%> 8 | <%-include itemExamples.ejs-%> -------------------------------------------------------------------------------- /docs/_ejs/contentLoop.ejs: -------------------------------------------------------------------------------- 1 | <%if(category.items) category.items.forEach(item => {-%> 2 | <%-include contentItem.ejs%> 3 | <%})-%> -------------------------------------------------------------------------------- /docs/_ejs/itemExamples.ejs: -------------------------------------------------------------------------------- 1 | <%if(item.examples && item.examples.length > 0){-%> 2 | 3 | #### Example<%if(item.examples.length > 1){%>s<%}%> 4 | <%item.examples.forEach(example => {-%> 5 | 6 | ```javascript 7 | <%-example%> 8 | ``` 9 | <%})-%> 10 | <%}-%> -------------------------------------------------------------------------------- /docs/_ejs/itemParameters.ejs: -------------------------------------------------------------------------------- 1 | <%if(item.paramsTable && item.paramsTable.length > 0){-%> 2 | 3 | #### Parameter<%if(item.params && item.params.length > 1){%>s<%}%> 4 | 5 | <%item.paramsTable.forEach(param => {-%> 6 | <%-param%> 7 | <%})-%> 8 | <%}-%> -------------------------------------------------------------------------------- /docs/_ejs/itemReturns.ejs: -------------------------------------------------------------------------------- 1 | <%if(item.returnsTable && item.returnsTable.length > 0){-%> 2 | 3 | #### Return 4 | 5 | <%item.returnsTable.forEach(param => {-%> 6 | <%-param%> 7 | <%})-%> 8 | <%}-%> -------------------------------------------------------------------------------- /docs/_ejs/itemTypeDefParams.ejs: -------------------------------------------------------------------------------- 1 | <%if(item.typedefParams && item.typedefParams.length > 0){-%> 2 | <%typedefs.filter(typedef => item.typedefParams.includes(typedef.name)).forEach(typedef => {-%> 3 | 4 | #### <%-typedef.nameCapitalized%> 5 | 6 | <%if(typedef.propertiesTable) typedef.propertiesTable.forEach(property => {-%> 7 | <%-property%> 8 | <%})-%> 9 | <%})-%> 10 | <%}-%> -------------------------------------------------------------------------------- /docs/_ejs/itemTypeDefReturns.ejs: -------------------------------------------------------------------------------- 1 | <%if(item.typedefReturns && item.typedefReturns.length > 0){-%> 2 | <%typedefs.filter(typedef => item.typedefReturns.includes(typedef.name)).forEach(typedef => {-%> 3 | 4 | #### <%-typedef.nameCapitalized%> 5 | 6 | <%if(typedef.propertiesTable) typedef.propertiesTable.forEach(property => {-%> 7 | <%-property%> 8 | <%})-%> 9 | <%})-%> 10 | <%}-%> -------------------------------------------------------------------------------- /docs/_ejs/menu.ejs: -------------------------------------------------------------------------------- 1 | <%if(docs) docs.forEach(category => {-%> 2 | * **[<%-category.name%>](#<%-category.href%>)** 3 | <%-include menuLoop.ejs-%> 4 | 5 | <%})-%> -------------------------------------------------------------------------------- /docs/_ejs/menuLoop.ejs: -------------------------------------------------------------------------------- 1 | <%if(category.items) category.items.forEach(item => {-%> 2 | * [<%-item.name%>](#<%-item.href%>) 3 | <%})-%> -------------------------------------------------------------------------------- /docs/_includes/about.html: -------------------------------------------------------------------------------- 1 | {% capture about %}{% include about.md %}{% endcapture %} 2 | {{ about | markdownify }} 3 | -------------------------------------------------------------------------------- /docs/_includes/about.md: -------------------------------------------------------------------------------- 1 | ## *About* 2 | 3 | Canivete (Brazilian Portuguese for swiss army knife) is an on-going personal project by [Leonardo Favre](https://leofavre.com/), a place to keep generic, multi-purpose Javascript functions. 4 | 5 | Due to its multi-purpose nature, Canivete is not distributed as a single file, instead, one should import its functions as needed, using [ES6 module syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import). 6 | 7 | [Babel](https://babeljs.io/) and [Babel Polyfill](https://babeljs.io/docs/usage/polyfill/) are recommended to make Canivete work in older browsers. 8 | 9 | [Canivete on GitHub](https://github.com/leofavre/canivete/) -------------------------------------------------------------------------------- /docs/_includes/canivete-with-rollup.html: -------------------------------------------------------------------------------- 1 | {% capture canivete-with-rollup %}{% include canivete-with-rollup.md %}{% endcapture %} 2 | {{ canivete-with-rollup | markdownify }} 3 | -------------------------------------------------------------------------------- /docs/_includes/canivete-with-rollup.md: -------------------------------------------------------------------------------- 1 | ### Using Canivete with [Rollup](https://rollupjs.org/) 2 | 3 | **1.** In your project root folder, install Canivete via NPM. 4 | 5 | ```shell 6 | $ npm install --save leofavre/canivete 7 | ``` 8 | 9 | **2.** Install Rollup globally via NPM. 10 | 11 | ```shell 12 | $ npm install --global rollup 13 | ``` 14 | 15 | **3.** Install the Node Resolve Pulgin for Rollup via NPM. 16 | 17 | ```shell 18 | $ npm install --save-dev rollup-plugin-node-resolve 19 | ``` 20 | 21 | **4.** Create a file named "rollup.config.js" with the following content: 22 | 23 | ```js 24 | import nodeResolve from "rollup-plugin-node-resolve"; 25 | 26 | export default { 27 | "entry": "./index.js", 28 | "dest": "./dist/app.js", 29 | "plugins": [nodeResolve()], 30 | "format": "es" 31 | }; 32 | ``` 33 | 34 | **5.** Create a file named "index.js". Import any dependencies from Canivete using ES6 module syntax before the rest of your code, like this: 35 | 36 | ```js 37 | import toAverage from "canivete/dist/toAverage"; 38 | 39 | const myArray = [8, 10, 12, 14, 16]; 40 | alert(myArray.reduce(toAverage)); 41 | // => 12 42 | ``` 43 | 44 | **6.** Use the following shell command to build your project: 45 | 46 | ```shell 47 | $ rollup -c 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/_includes/canivete-with-webpack.html: -------------------------------------------------------------------------------- 1 | {% capture canivete-with-webpack %}{% include canivete-with-webpack.md %}{% endcapture %} 2 | {{ canivete-with-webpack | markdownify }} 3 | -------------------------------------------------------------------------------- /docs/_includes/canivete-with-webpack.md: -------------------------------------------------------------------------------- 1 | ### Using Canivete with [Webpack](https://webpack.js.org/) 2 | 3 | **1.** In your project root folder, install Canivete via NPM. 4 | 5 | ```shell 6 | $ npm install --save leofavre/canivete 7 | ``` 8 | 9 | **2.** Install Webpack via NPM. 10 | 11 | ```shell 12 | $ npm install --save-dev webpack 13 | ``` 14 | 15 | **3.** Create a file named "webpack.config.js" with the following content: 16 | 17 | ```js 18 | const path = require("path"); 19 | 20 | module.exports = { 21 | "entry": "./index.js", 22 | "output": { 23 | "path": path.resolve(__dirname, "dist"), 24 | "filename": "app.js" 25 | } 26 | }; 27 | ``` 28 | 29 | **4.** Create a file named "index.js". Import any dependencies from Canivete using ES6 module syntax before the rest of your code, like this: 30 | 31 | ```js 32 | import toAverage from "canivete/dist/toAverage"; 33 | 34 | const myArray = [8, 10, 12, 14, 16]; 35 | alert(myArray.reduce(toAverage)); 36 | // => 12 37 | ``` 38 | 39 | **5.** Use the following shell command to build your project: 40 | 41 | ```shell 42 | $ ./node_modules/.bin/webpack 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/_includes/credits.html: -------------------------------------------------------------------------------- 1 | {% capture credits %}{% include credits.md %}{% endcapture %} 2 | {{ credits | markdownify }} 3 | -------------------------------------------------------------------------------- /docs/_includes/credits.md: -------------------------------------------------------------------------------- 1 | ## Credits 2 | 3 | * **Canivete** Coding and documentation by [Leonardo Favre](https://leofavre.com/) 4 | * **Website** Coding and layout by [Leonardo Favre](https://leofavre.com/) and symbol by [Andre Levy a.k.a. Zhion](http://www.zhion.com.br) 5 | * **Typography** [Acropolis](https://www.typography.com/fonts/acropolis/overview/) and [Whitney](https://www.typography.com/fonts/whitney/overview/) by [Hoefler & Co.](https://www.typography.com/) and [Fira Code](https://github.com/tonsky/FiraCode) by [Nikita Prokopov](https://github.com/tonsky/) 6 | 7 | Canivete is released under The Unlicense license. -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/_includes/header.html: -------------------------------------------------------------------------------- 1 | {{ page.title }} -------------------------------------------------------------------------------- /docs/_includes/jquery.scrollTo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2015 Ariel Flesler - aflesler ○ gmail • com | http://flesler.blogspot.com 3 | * Licensed under MIT 4 | * @author Ariel Flesler 5 | * @version 2.1.3 6 | */ 7 | ;(function(f){"use strict";"function"===typeof define&&define.amd?define(["jquery"],f):"undefined"!==typeof module&&module.exports?module.exports=f(require("jquery")):f(jQuery)})(function($){"use strict";function n(a){return!a.nodeName||-1!==$.inArray(a.nodeName.toLowerCase(),["iframe","#document","html","body"])}function h(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}var p=$.scrollTo=function(a,d,b){return $(window).scrollTo(a,d,b)};p.defaults={axis:"xy",duration:0,limit:!0};$.fn.scrollTo=function(a,d,b){"object"=== typeof d&&(b=d,d=0);"function"===typeof b&&(b={onAfter:b});"max"===a&&(a=9E9);b=$.extend({},p.defaults,b);d=d||b.duration;var u=b.queue&&1=f[g]?0:Math.min(f[g],n));!a&&1 2 | About 3 | Usage 4 | Credits 5 | 6 | -------------------------------------------------------------------------------- /docs/_includes/usage.html: -------------------------------------------------------------------------------- 1 | {% capture usage %}{% include usage.md %}{% endcapture %} 2 | {{ usage | markdownify }} 3 | -------------------------------------------------------------------------------- /docs/_includes/usage.md: -------------------------------------------------------------------------------- 1 | ## *Usage* 2 | 3 | Since ES6 modules have virtually no browser support at this time, a module bundler is needed to handle Canivete dependencies. 4 | 5 | Here's how to use Canivete with [Rollup](#using-canivete-with-rollup) and [Webpack](#using-canivete-with-webpack). 6 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | 6 | 7 | 8 | Canivete 9 | 10 | 11 | {% include pagenav.html %} 12 | {% include menu.html %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% include about.html %} 21 | {% include usage.html %} 22 | {% include canivete-with-rollup.html %} 23 | {% include canivete-with-webpack.html %} 24 | {% include credits.html %} 25 | 26 | 27 | {{ content }} 28 | 29 | 30 | 31 | 32 | {% include footer.html %} 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/_sass/_grid.scss: -------------------------------------------------------------------------------- 1 | %grid { 2 | background-position: 0.3125rem 0; 3 | background-size: $grid $grid; 4 | background-image: linear-gradient(to bottom, lightblue 1px, transparent 1px); 5 | background-attachment: scroll; 6 | } 7 | -------------------------------------------------------------------------------- /docs/_sass/_html.scss: -------------------------------------------------------------------------------- 1 | html { 2 | height: auto; 3 | font-size: 16px; 4 | -ms-text-size-adjust: 100%; 5 | -webkit-text-size-adjust: 100%; 6 | -webkit-tap-highlight-color: rgba(0,0,0,0); 7 | -webkit-tap-highlight-color: transparent; 8 | 9 | @media #{$bigger-than-big-screen} { 10 | font-size: 18px; 11 | } 12 | 13 | @media #{$smaller-than-landscape-phone} { 14 | font-size: 15px; 15 | } 16 | } 17 | 18 | html.mode--native-scroll { 19 | height: auto; 20 | } 21 | 22 | html.mode--local-scroll { 23 | height: 100%; 24 | } -------------------------------------------------------------------------------- /docs/_sass/_page.scss: -------------------------------------------------------------------------------- 1 | .page { 2 | -webkit-font-smoothing: antialiased; 3 | -moz-osx-font-smoothing: grayscale; 4 | font-family: "Whitney SSm A", "Whitney SSm B", sans-serif; 5 | 6 | margin: 0; 7 | color: $text-color; 8 | background: $background-color; 9 | 10 | height: auto; 11 | 12 | .mode--native-scroll & { 13 | height: auto; 14 | } 15 | 16 | .mode--local-scroll & { 17 | height: 100%; 18 | } 19 | } 20 | 21 | .page__inner { 22 | position: relative; 23 | display: block; 24 | margin: 0 auto; 25 | width: 100%; 26 | height: auto; 27 | overflow: auto; 28 | 29 | -webkit-overflow-scrolling: touch; 30 | -ms-overflow-style: -ms-autohiding-scrollbar; 31 | 32 | .mode--native-scroll & { 33 | height: auto; 34 | overflow: auto; 35 | } 36 | 37 | .mode--local-scroll & { 38 | height: 100%; 39 | overflow-x: hidden; 40 | overflow-y: auto; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/_sass/_scrollbar.scss: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | width: 6px; 3 | height: 6px; 4 | } 5 | 6 | ::-webkit-scrollbar-button { 7 | width: 0px; 8 | height: 0px; 9 | } 10 | 11 | ::-webkit-scrollbar-thumb { 12 | background: $text; 13 | border: none; 14 | border-radius: 6px; 15 | } 16 | 17 | ::-webkit-scrollbar-thumb:hover { 18 | background: $text; 19 | } 20 | 21 | ::-webkit-scrollbar-thumb:active { 22 | background: $text; 23 | } 24 | 25 | ::-webkit-scrollbar-track { 26 | background: transparent; 27 | border: none; 28 | } 29 | 30 | ::-webkit-scrollbar-track:hover { 31 | background: transparent; 32 | } 33 | 34 | ::-webkit-scrollbar-track:active { 35 | background: transparent; 36 | } 37 | 38 | ::-webkit-scrollbar-corner { 39 | background: transparent; 40 | } 41 | -------------------------------------------------------------------------------- /docs/_sass/_typography.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 16px; 3 | } 4 | 5 | @font-face{ 6 | font-family: "Fira Code"; 7 | src: url("../fonts/eot/FiraCode-Regular.eot"); 8 | src: url("../fonts/eot/FiraCode-Regular.eot") format("embedded-opentype"), 9 | url("../fonts/woff2/FiraCode-Regular.woff2") format("woff2"), 10 | url("../fonts/woff/FiraCode-Regular.woff") format("woff"), 11 | url("../fonts/ttf/FiraCode-Regular.ttf") format("truetype"); 12 | font-weight: 400; 13 | font-style: normal; 14 | } 15 | 16 | @font-face{ 17 | font-family: "Fira Code"; 18 | src: url("../fonts/eot/FiraCode-Medium.eot"); 19 | src: url("../fonts/eot/FiraCode-Medium.eot") format("embedded-opentype"), 20 | url("../fonts/woff2/FiraCode-Medium.woff2") format("woff2"), 21 | url("../fonts/woff/FiraCode-Medium.woff") format("woff"), 22 | url("../fonts/ttf/FiraCode-Medium.ttf") format("truetype"); 23 | font-weight: 500; 24 | font-style: normal; 25 | } -------------------------------------------------------------------------------- /docs/_sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $grid: 1.5rem; 2 | $full-content-width: 42rem; 3 | $nav-width: 14.5rem; 4 | $nav-mobile-height: 40px; 5 | 6 | $content-left-padding: ($grid * 3); 7 | $content-right-padding: ($grid * 2); 8 | 9 | 10 | // breakpoints 11 | 12 | $way-bigger-than-big-screen: "only screen and (min-width: 1672px)"; 13 | $bigger-than-big-screen: "only screen and (min-width: 1400px)"; 14 | $bigger-than-desktop: "only screen and (min-width: 1101px)"; 15 | $smaller-than-desktop: "only screen and (max-width: 1100px)"; 16 | $bigger-than-landscape-tablet: "only screen and (min-width: 1024px)"; 17 | $smaller-than-landscape-tablet: "only screen and (max-width: 1023px)"; 18 | $content-touches-menu: "only screen and (max-width: #{$nav-width + $full-content-width + $content-left-padding + $content-right-padding})"; 19 | $content-almost-touches-margins: "only screen and (max-width: 820px)"; 20 | $smaller-than-portrait-tablet: "only screen and (max-width: 700px)"; 21 | $smaller-than-landscape-phone: "only screen and (max-width: 550px)"; 22 | $portrait-phone: "only screen and (max-width: 400px)"; 23 | 24 | 25 | // bubble gum color scheme 26 | 27 | $background-color: mix(#fbfbf7, white, 100%); 28 | $text-color: #3d464d; 29 | 30 | $logo-color: #fb1e70; 31 | $logo-hover-color: #c02e75; 32 | 33 | $intro-h2-color: $text-color; 34 | $intro-h2-small-color: $text-color; 35 | $intro-h3-color: $text-color; 36 | 37 | $button-color: $logo-color; 38 | $button-hover-color: $logo-hover-color; 39 | 40 | $content-h2-color: #c7efef; 41 | $content-h2-small-color: mix(#7ae1e6, $content-h2-color, 15%); 42 | $content-h3-color: #7ae1e6; 43 | $content-h3-bg-color: $text-color; 44 | 45 | $small-code-color: $text-color; 46 | $small-code-bg-color: $content-h2-color; 47 | $optional-color: $background-color; 48 | $optional-bg-color: $text-color; 49 | 50 | $link-hairline-color: $logo-color; 51 | $link-hover-color: rgba($link-hairline-color, 0.2); 52 | 53 | $menu-hairline-color: mix($text-color, $background-color, 50%); 54 | $table-hairline-color: $menu-hairline-color; 55 | 56 | $big-code-color: $text-color; 57 | $big-code-bg-color: mix(#f0f3f5, black, 95%); 58 | 59 | $code-blue: #07a; 60 | $code-red: $logo-color; 61 | $code-str: #46a805; 62 | $code-purple: #813dff; 63 | $comment: $menu-hairline-color; 64 | 65 | $menu-item-color: $text-color; 66 | $menu-item-selected-bg: $big-code-bg-color; 67 | $menu-item-hover-bg: mix($menu-item-selected-bg, $background-color, 25%); 68 | 69 | $nav-mobile-header-background: #32383e; 70 | $nav-switch-color: $background-color; 71 | -------------------------------------------------------------------------------- /docs/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #32383e 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/canivete.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/canivete.jpg -------------------------------------------------------------------------------- /docs/canivete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/canivete.png -------------------------------------------------------------------------------- /docs/css/kern.css: -------------------------------------------------------------------------------- 1 | /* about */ 2 | 3 | #about { 4 | -letter-kern: 0.01em -0.01em -0.02em 0em 0; 5 | } 6 | 7 | /* usage */ 8 | 9 | #usage { 10 | -letter-kern: 0.03em -0.02em -0.01em -0.015em 0; 11 | } 12 | 13 | /* credits */ 14 | 15 | #credits { 16 | -letter-kern: 0 -0.02em -0.03em 0em -0.02em 0 0; 17 | } 18 | 19 | /* bem */ 20 | 21 | #bem { 22 | -letter-kern: -0.01em 0 0; 23 | } 24 | 25 | /* classname */ 26 | 27 | #classname { 28 | -letter-kern: -0.05em -0.01em 0.015em -0.01em 0.015em -0.01em -0.015em -0.025em 0; 29 | } 30 | 31 | /* collection */ 32 | 33 | #collection { 34 | -letter-kern: -0.04em -0.04em 0 -0.03em -0.035em -0.035em 0.035em -0.035em -0.02em 0; 35 | } 36 | 37 | /* dom */ 38 | 39 | #dom { 40 | -letter-kern: -0.02em -0.02em 0; 41 | } 42 | 43 | /* event */ 44 | 45 | #event { 46 | -letter-kern: -0.01em -0.03em 0 -0.02em 0; 47 | } 48 | 49 | /* geometry */ 50 | 51 | #geometry { 52 | -letter-kern: -0.015em -0.035em -0.02em -0.025em -0.03em 0.035em 0 0; 53 | } 54 | 55 | #geometry .char4 { 56 | margin-left: 0.42em; 57 | } 58 | 59 | #geometry .char6 { 60 | margin-left: -0.09em; 61 | } 62 | 63 | /* promise */ 64 | 65 | #promise { 66 | -letter-kern: 0 -0.03em -0.02em -0.02em 0.01em -0.015em 0; 67 | } 68 | 69 | #promise .char4 { 70 | margin-left: 0.32em; 71 | } 72 | 73 | /* random */ 74 | 75 | #random { 76 | -letter-kern: -0.03em -0.01em 0 -0.02em -0.02em 0; 77 | } 78 | 79 | #random .char4 { 80 | margin-left: -0.07em; 81 | } 82 | 83 | /* reduce */ 84 | 85 | #reduce { 86 | -letter-kern: -0.03em -0.03em 0 0 -0.04em 0; 87 | } 88 | 89 | /* sort */ 90 | 91 | #sort { 92 | -letter-kern: -0.01em -0.025em 0.01em 0; 93 | } 94 | 95 | /* string */ 96 | 97 | #string { 98 | -letter-kern: 0 0.035em 0 0 0 0; 99 | } 100 | 101 | /* time */ 102 | 103 | #time { 104 | -letter-kern: 0.04em -0.02em -0.025em 0; 105 | } 106 | -------------------------------------------------------------------------------- /docs/css/styles.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | @import "variables"; 4 | 5 | @import "typography"; 6 | @import "html"; 7 | @import "page"; 8 | @import "grid"; 9 | 10 | @import "nav"; 11 | @import "content"; 12 | 13 | @import "monokai"; 14 | -------------------------------------------------------------------------------- /docs/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/favicon-16x16.png -------------------------------------------------------------------------------- /docs/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/favicon-32x32.png -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/favicon.ico -------------------------------------------------------------------------------- /docs/fonts/685260/035AEABEDE40158BE.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/035AEABEDE40158BE.eot -------------------------------------------------------------------------------- /docs/fonts/685260/073C176AF68C71AB5.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/073C176AF68C71AB5.eot -------------------------------------------------------------------------------- /docs/fonts/685260/1155D4E37D66DB051.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/1155D4E37D66DB051.eot -------------------------------------------------------------------------------- /docs/fonts/685260/4041214EAB7F284B4.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (C) 2011-2018 Hoefler & Co. 4 | This software is the property of Hoefler & Co. (H&Co). 5 | Your right to access and use this software is subject to the 6 | applicable License Agreement, or Terms of Service, that exists 7 | between you and H&Co. If no such agreement exists, you may not 8 | access or use this software for any purpose. 9 | This software may only be hosted at the locations specified in 10 | the applicable License Agreement or Terms of Service, and only 11 | for the purposes expressly set forth therein. You may not copy, 12 | modify, convert, create derivative works from or distribute this 13 | software in any way, or make it accessible to any third party, 14 | without first obtaining the written permission of H&Co. 15 | For more information, please visit us at http://typography.com. 16 | 316729-117071-20180907 17 | */ 18 | -------------------------------------------------------------------------------- /docs/fonts/685260/40D4B15013983F3B9.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/40D4B15013983F3B9.eot -------------------------------------------------------------------------------- /docs/fonts/685260/449A5CFFBB492D3C1.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/449A5CFFBB492D3C1.eot -------------------------------------------------------------------------------- /docs/fonts/685260/4C5D90B170D6E7C2A.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/4C5D90B170D6E7C2A.eot -------------------------------------------------------------------------------- /docs/fonts/685260/6BE8C6AFC5BFAB166.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/6BE8C6AFC5BFAB166.eot -------------------------------------------------------------------------------- /docs/fonts/685260/71ED6E15CBE60A2C2.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/71ED6E15CBE60A2C2.eot -------------------------------------------------------------------------------- /docs/fonts/685260/7F8B387F8F2396EB0.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/7F8B387F8F2396EB0.eot -------------------------------------------------------------------------------- /docs/fonts/685260/83FB9B1DA953E306B.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/83FB9B1DA953E306B.eot -------------------------------------------------------------------------------- /docs/fonts/685260/89521A4AC6B3DD4E2.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (C) 2011-2018 Hoefler & Co. 4 | This software is the property of Hoefler & Co. (H&Co). 5 | Your right to access and use this software is subject to the 6 | applicable License Agreement, or Terms of Service, that exists 7 | between you and H&Co. If no such agreement exists, you may not 8 | access or use this software for any purpose. 9 | This software may only be hosted at the locations specified in 10 | the applicable License Agreement or Terms of Service, and only 11 | for the purposes expressly set forth therein. You may not copy, 12 | modify, convert, create derivative works from or distribute this 13 | software in any way, or make it accessible to any third party, 14 | without first obtaining the written permission of H&Co. 15 | For more information, please visit us at http://typography.com. 16 | 316729-117071-20180907 17 | */ 18 | -------------------------------------------------------------------------------- /docs/fonts/685260/A476E19F63A389192.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/A476E19F63A389192.eot -------------------------------------------------------------------------------- /docs/fonts/685260/AE4FC5648EFBC108A.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/AE4FC5648EFBC108A.eot -------------------------------------------------------------------------------- /docs/fonts/685260/B633E65E15A793E2B.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/B633E65E15A793E2B.eot -------------------------------------------------------------------------------- /docs/fonts/685260/E70BA8416428E1CDA.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/685260/E70BA8416428E1CDA.eot -------------------------------------------------------------------------------- /docs/fonts/eot/FiraCode-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/eot/FiraCode-Bold.eot -------------------------------------------------------------------------------- /docs/fonts/eot/FiraCode-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/eot/FiraCode-Light.eot -------------------------------------------------------------------------------- /docs/fonts/eot/FiraCode-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/eot/FiraCode-Medium.eot -------------------------------------------------------------------------------- /docs/fonts/eot/FiraCode-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/eot/FiraCode-Regular.eot -------------------------------------------------------------------------------- /docs/fonts/fira_code.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: "Fira Code"; 3 | src: url("../fonts/eot/FiraCode-Light.eot"); 4 | src: url("../fonts/eot/FiraCode-Light.eot") format("embedded-opentype"), 5 | url("../fonts/woff2/FiraCode-Light.woff2") format("woff2"), 6 | url("../fonts/woff/FiraCode-Light.woff") format("woff"), 7 | url("../fonts/ttf/FiraCode-Light.ttf") format("truetype"); 8 | font-weight: 300; 9 | font-style: normal; 10 | } 11 | 12 | @font-face{ 13 | font-family: "Fira Code"; 14 | src: url("../fonts/eot/FiraCode-Regular.eot"); 15 | src: url("../fonts/eot/FiraCode-Regular.eot") format("embedded-opentype"), 16 | url("../fonts/woff2/FiraCode-Regular.woff2") format("woff2"), 17 | url("../fonts/woff/FiraCode-Regular.woff") format("woff"), 18 | url("../fonts/ttf/FiraCode-Regular.ttf") format("truetype"); 19 | font-weight: 400; 20 | font-style: normal; 21 | } 22 | 23 | @font-face{ 24 | font-family: "Fira Code"; 25 | src: url("../fonts/eot/FiraCode-Medium.eot"); 26 | src: url("../fonts/eot/FiraCode-Medium.eot") format("embedded-opentype"), 27 | url("../fonts/woff2/FiraCode-Medium.woff2") format("woff2"), 28 | url("../fonts/woff/FiraCode-Medium.woff") format("woff"), 29 | url("../fonts/ttf/FiraCode-Medium.ttf") format("truetype"); 30 | font-weight: 500; 31 | font-style: normal; 32 | } 33 | 34 | @font-face{ 35 | font-family: "Fira Code"; 36 | src: url("../fonts/eot/FiraCode-Bold.eot"); 37 | src: url("../fonts/eot/FiraCode-Bold.eot") format("embedded-opentype"), 38 | url("../fonts/woff2/FiraCode-Bold.woff2") format("woff2"), 39 | url("../fonts/woff/FiraCode-Bold.woff") format("woff"), 40 | url("../fonts/ttf/FiraCode-Bold.ttf") format("truetype"); 41 | font-weight: 700; 42 | font-style: normal; 43 | } -------------------------------------------------------------------------------- /docs/fonts/otf/FiraCode-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/otf/FiraCode-Bold.otf -------------------------------------------------------------------------------- /docs/fonts/otf/FiraCode-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/otf/FiraCode-Light.otf -------------------------------------------------------------------------------- /docs/fonts/otf/FiraCode-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/otf/FiraCode-Medium.otf -------------------------------------------------------------------------------- /docs/fonts/otf/FiraCode-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/otf/FiraCode-Regular.otf -------------------------------------------------------------------------------- /docs/fonts/otf/FiraCode-Retina.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/otf/FiraCode-Retina.otf -------------------------------------------------------------------------------- /docs/fonts/specimen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fira Code Specimen 7 | 8 | 9 | 26 | 27 | 28 | # Fira Code Light 29 | 30 | take = (n, [x, ...xs]:list) --> 31 | | n <= 0 => [] 32 | | empty list => [] 33 | | otherwise => [x] ++ take n-1, xs 34 | 35 | last3 = reverse >> take 3 >> reverse 36 | 37 | 38 | # Fira Code Regular 39 | 40 | take = (n, [x, ...xs]:list) --> 41 | | n <= 0 => [] 42 | | empty list => [] 43 | | otherwise => [x] ++ take n-1, xs 44 | 45 | last3 = reverse >> take 3 >> reverse 46 | 47 | 48 | # Fira Code Medium 49 | 50 | take = (n, [x, ...xs]:list) --> 51 | | n <= 0 => [] 52 | | empty list => [] 53 | | otherwise => [x] ++ take n-1, xs 54 | 55 | last3 = reverse >> take 3 >> reverse 56 | 57 | 58 | # Fira Code Bold 59 | 60 | take = (n, [x, ...xs]:list) --> 61 | | n <= 0 => [] 62 | | empty list => [] 63 | | otherwise => [x] ++ take n-1, xs 64 | 65 | last3 = reverse >> take 3 >> reverse 66 | -------------------------------------------------------------------------------- /docs/fonts/ttf/FiraCode-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/ttf/FiraCode-Bold.ttf -------------------------------------------------------------------------------- /docs/fonts/ttf/FiraCode-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/ttf/FiraCode-Light.ttf -------------------------------------------------------------------------------- /docs/fonts/ttf/FiraCode-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/ttf/FiraCode-Medium.ttf -------------------------------------------------------------------------------- /docs/fonts/ttf/FiraCode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/ttf/FiraCode-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/ttf/FiraCode-Retina.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/ttf/FiraCode-Retina.ttf -------------------------------------------------------------------------------- /docs/fonts/woff/FiraCode-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff/FiraCode-Bold.woff -------------------------------------------------------------------------------- /docs/fonts/woff/FiraCode-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff/FiraCode-Light.woff -------------------------------------------------------------------------------- /docs/fonts/woff/FiraCode-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff/FiraCode-Medium.woff -------------------------------------------------------------------------------- /docs/fonts/woff/FiraCode-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff/FiraCode-Regular.woff -------------------------------------------------------------------------------- /docs/fonts/woff2/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff2/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /docs/fonts/woff2/FiraCode-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff2/FiraCode-Light.woff2 -------------------------------------------------------------------------------- /docs/fonts/woff2/FiraCode-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff2/FiraCode-Medium.woff2 -------------------------------------------------------------------------------- /docs/fonts/woff2/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/fonts/woff2/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Canivete", 3 | "icons": [ 4 | { 5 | "src": "/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } -------------------------------------------------------------------------------- /docs/media/canivete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 12 | 13 | 15 | 16 | 17 | 19 | 20 | 22 | 24 | 26 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/media/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /docs/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/mstile-144x144.png -------------------------------------------------------------------------------- /docs/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/mstile-150x150.png -------------------------------------------------------------------------------- /docs/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/mstile-310x150.png -------------------------------------------------------------------------------- /docs/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/mstile-310x310.png -------------------------------------------------------------------------------- /docs/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leofavre/canivete/7c94cf07e441b7472db317429983371c8c108d6c/docs/mstile-70x70.png -------------------------------------------------------------------------------- /docs/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | process.env.CHROME_BIN = require('puppeteer').executablePath(); 4 | 5 | module.exports = (config) => { 6 | config.set({ 7 | browsers: [ 8 | 'ChromeHeadlessNoSandbox', 9 | 'FirefoxHeadless' 10 | ], 11 | customLaunchers: { 12 | ChromeHeadlessNoSandbox: { 13 | base: 'ChromeHeadless', 14 | flags: ['--no-sandbox'] 15 | }, 16 | FirefoxHeadless: { 17 | base: 'Firefox', 18 | flags: ['-headless'] 19 | } 20 | }, 21 | browserNoActivityTimeout: 60000, 22 | singleRun: true, 23 | frameworks: ['jasmine'], 24 | files: [ 25 | { 26 | pattern: 'test/*.js', 27 | watched: false 28 | } 29 | ], 30 | preprocessors: { 31 | 'test/*.js': ['webpack'] 32 | }, 33 | webpack: { 34 | mode: 'development', 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.js$/, 39 | use: { 40 | loader: 'istanbul-instrumenter-loader', 41 | options: { 42 | esModules: true 43 | } 44 | }, 45 | include: path.resolve(__dirname, 'test'), 46 | exclude: /((node_modules|docs|dist)(\\|\/|$)|test\.js$)/ 47 | } 48 | ] 49 | } 50 | }, 51 | reporters: ['coverage-istanbul'], 52 | coverageIstanbulReporter: { 53 | reports: ['html', 'lcovonly', 'text-summary'], 54 | fixWebpackSourcePaths: true, 55 | skipFilesWithNoCoverage: true 56 | } 57 | }); 58 | }; 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "canivete", 3 | "version": "2.2.0", 4 | "description": "Canivete Javascript toolkit", 5 | "scripts": { 6 | "test": "karma start", 7 | "docs": "node ./docs/_script/run" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/leofavre/canivete.git" 12 | }, 13 | "author": "Leonardo Favre (http://www.leofavre.com/)", 14 | "license": "Unlicense", 15 | "bugs": { 16 | "url": "https://github.com/leofavre/canivete/issues" 17 | }, 18 | "homepage": "https://github.com/leofavre/canivete#readme", 19 | "devDependencies": { 20 | "babel-plugin-external-helpers": "^6.22.0", 21 | "babel-preset-es2015": "^6.24.1", 22 | "ejs-cli": "^2.0.1", 23 | "istanbul-instrumenter-loader": "^3.0.1", 24 | "jasmine": "~2.3.2", 25 | "jasmine-core": "~2.3.4", 26 | "jsdoc": "^3.5.5", 27 | "jsdoc-json": "^2.0.0", 28 | "jsdocs": "^0.1.0", 29 | "karma": "~0.13.15", 30 | "karma-chrome-launcher": "^2.2.0", 31 | "karma-coverage-istanbul-reporter": "^1.4.2", 32 | "karma-firefox-launcher": "^1.1.0", 33 | "karma-jasmine": "~0.3.6", 34 | "karma-sourcemap-loader": "^0.3.7", 35 | "karma-webpack": "^3.0.0", 36 | "lodash": "^4.17.4", 37 | "lodash-es": "^4.17.4", 38 | "puppeteer": "^1.8.0", 39 | "rollup": "^0.41.6", 40 | "rollup-plugin-babel": "^2.7.1", 41 | "rollup-plugin-multi-entry": "^2.0.1", 42 | "rollup-plugin-node-resolve": "^3.0.0", 43 | "rollup-plugin-uglify": "^2.0.0", 44 | "webpack": "^4.6.0" 45 | }, 46 | "dependencies": {} 47 | } 48 | -------------------------------------------------------------------------------- /rollup.docs.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from "rollup-plugin-node-resolve"; 2 | import babel from "rollup-plugin-babel"; 3 | // import uglify from "rollup-plugin-uglify"; 4 | 5 | export default { 6 | entry: "./docs/index.js", 7 | dest: "./docs/js/index.js", 8 | plugins: [ 9 | babel({ 10 | "presets": [ 11 | [ 12 | "es2015", 13 | { 14 | "modules": false 15 | } 16 | ] 17 | ], 18 | "plugins": [ 19 | "external-helpers" 20 | ] 21 | }), 22 | nodeResolve({ 23 | jsnext: true 24 | }), 25 | // uglify() 26 | ], 27 | format: "es", 28 | context: "this" 29 | }; -------------------------------------------------------------------------------- /rollup.test.config.js: -------------------------------------------------------------------------------- 1 | import multiEntry from "rollup-plugin-multi-entry"; 2 | import nodeResolve from "rollup-plugin-node-resolve"; 3 | 4 | export default { 5 | entry: "./test/*.js", 6 | dest: "./test/temp/allSpecs.js", 7 | plugins: [ 8 | multiEntry(), 9 | nodeResolve({ 10 | jsnext: true 11 | }) 12 | ], 13 | format: "es" 14 | }; 15 | -------------------------------------------------------------------------------- /test/_domElementsAsArraySpecs.js: -------------------------------------------------------------------------------- 1 | import _domElementsToArray from "../dist/internal/dom/_domElementsToArray.js"; 2 | 3 | describe("_domElementsToArray", function() { 4 | for (let i = 0; i < 5; i++) { 5 | let newEl = document.createElement("div"); 6 | newEl.className = "domElementsToArray domElementsToArray-" + i; 7 | document.body.appendChild(newEl); 8 | } 9 | 10 | it("Should convert a single HTMLElement to an Array.", function() { 11 | let singleHTMLElement = document.querySelector(".domElementsToArray-1"); 12 | let elementsArray = _domElementsToArray(singleHTMLElement); 13 | 14 | expect(Array.isArray(singleHTMLElement)).toBe(false); 15 | expect(Array.isArray(elementsArray)).toBe(true); 16 | expect(elementsArray.length).toBe(1); 17 | }); 18 | 19 | it("Should convert a NodeList with many elements to an Array.", function() { 20 | let elementsNodeList = document.querySelectorAll(".domElementsToArray"); 21 | let elementsArray = _domElementsToArray(elementsNodeList); 22 | 23 | expect(Array.isArray(elementsNodeList)).toBe(false); 24 | expect(Array.isArray(elementsArray)).toBe(true); 25 | expect(elementsArray.length).toBe(5); 26 | }); 27 | 28 | it("Should convert a NodeList with one element to an Array.", function() { 29 | let elementsNodeList = document.querySelectorAll(".domElementsToArray-1"); 30 | let elementsArray = _domElementsToArray(elementsNodeList); 31 | 32 | expect(Array.isArray(elementsNodeList)).toBe(false); 33 | expect(Array.isArray(elementsArray)).toBe(true); 34 | expect(elementsArray.length).toBe(1); 35 | }); 36 | 37 | it("Should convert an HTMLCollection with many elements to an Array.", function() { 38 | let elementsHTMLCollection = document.getElementsByClassName("domElementsToArray"); 39 | let elementsArray = _domElementsToArray(elementsHTMLCollection); 40 | 41 | expect(Array.isArray(elementsHTMLCollection)).toBe(false); 42 | expect(Array.isArray(elementsArray)).toBe(true); 43 | expect(elementsArray.length).toBe(5); 44 | }); 45 | 46 | it("Should convert an HTMLCollection with one element to an Array.", function() { 47 | let elementsHTMLCollection = document.getElementsByClassName("domElementsToArray-1"); 48 | let elementsArray = _domElementsToArray(elementsHTMLCollection); 49 | 50 | expect(Array.isArray(elementsHTMLCollection)).toBe(false); 51 | expect(Array.isArray(elementsArray)).toBe(true); 52 | expect(elementsArray.length).toBe(1); 53 | }); 54 | 55 | it("Should allow an Array of HTMLElements as parameter.", function() { 56 | let elementsHTMLElementArray = [document.querySelector(".domElementsToArray-1"), document.querySelector(".domElementsToArray-2")]; 57 | let elementsArray = _domElementsToArray(elementsHTMLElementArray); 58 | 59 | expect(Array.isArray(elementsHTMLElementArray)).toBe(true); 60 | expect(Array.isArray(elementsArray)).toBe(true); 61 | expect(elementsArray.length).toBe(2); 62 | }); 63 | 64 | it("Should allow a Set of HTMLElements as parameter.", function() { 65 | let elementsHTMLElementSet = new Set(); 66 | elementsHTMLElementSet.add(document.querySelector(".domElementsToArray-1")); 67 | elementsHTMLElementSet.add(document.querySelector(".domElementsToArray-2")); 68 | let elementsArray = _domElementsToArray(elementsHTMLElementSet); 69 | 70 | expect(Array.isArray(elementsHTMLElementSet)).toBe(false); 71 | expect(Array.isArray(elementsArray)).toBe(true); 72 | expect(elementsArray.length).toBe(2); 73 | }); 74 | 75 | it("Should accept undefined, null, empty Arrays or empty Sets as parameters.", function() { 76 | let emptySet = new Set(); 77 | 78 | expect(() => _domElementsToArray()).not.toThrow(); 79 | expect(() => _domElementsToArray([])).not.toThrow(); 80 | expect(() => _domElementsToArray(emptySet)).not.toThrow(); 81 | }); 82 | 83 | it("Should throw an error if the parameter is not empty, an HTMLElement or a group of HTMLElements.", function() { 84 | expect(() => _domElementsToArray(125)).toThrow(); 85 | expect(() => _domElementsToArray("String")).toThrow(); 86 | expect(() => _domElementsToArray({})).toThrow(); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/_getCoordsSpecs.js: -------------------------------------------------------------------------------- 1 | import _getCoords from "../dist/internal/geometry/_getCoords.js"; 2 | 3 | describe("_getCoords", function() { 4 | let maskEl = document.createElement("div"); 5 | maskEl.style.cssText = "position: fixed; top: 250px; left: 150px; width: 150px; height: 300px; display: block; background: rgba(153, 0, 153, 0.3);"; 6 | document.body.appendChild(maskEl); 7 | 8 | it("Should be able to get coordinates from a DOMElement", function() { 9 | expect(_getCoords(maskEl).top).toBe(250); 10 | expect(_getCoords(maskEl).bottom).toBe(550); 11 | expect(_getCoords(maskEl).left).toBe(150); 12 | expect(_getCoords(maskEl).right).toBe(300); 13 | }); 14 | 15 | let maskRect = maskEl.getBoundingClientRect(); 16 | 17 | it("Should be able to get coordinates from a DOMRect", function() { 18 | expect(_getCoords(maskRect).top).toBe(250); 19 | expect(_getCoords(maskRect).bottom).toBe(550); 20 | expect(_getCoords(maskRect).left).toBe(150); 21 | expect(_getCoords(maskRect).right).toBe(300); 22 | }); 23 | 24 | let maskObj = { 25 | top: 250, 26 | bottom: 550, 27 | left: 150, 28 | right: 300 29 | }; 30 | 31 | it("Should be able to get coordinates from a plain Object", function() { 32 | expect(_getCoords(maskObj).top).toBe(250); 33 | expect(_getCoords(maskObj).bottom).toBe(550); 34 | expect(_getCoords(maskObj).left).toBe(150); 35 | expect(_getCoords(maskObj).right).toBe(300); 36 | }); 37 | 38 | let wrongObj = { 39 | top: "250px", 40 | bottom: "550px", 41 | left: "150px", 42 | right: "300px" 43 | }; 44 | 45 | let otherWrongObj = { 46 | top: 250, 47 | bottom: 550 48 | }; 49 | 50 | it("Should throw an error if the second parameter is not undefined, a DOM element or an object like a DOMRect (containing top, bottom, left and right parameters).", function() { 51 | expect(() => _getCoords(wrongObj)).toThrow(); 52 | expect(() => _getCoords(otherWrongObj)).toThrow(); 53 | expect(() => _getCoords(189)).toThrow(); 54 | expect(() => _getCoords("String")).toThrow(); 55 | expect(() => _getCoords([250, 350, 150, 300])).toThrow(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/_isBooleanSpecs.js: -------------------------------------------------------------------------------- 1 | import _isBoolean from "../dist/internal/helpers/_isBoolean.js"; 2 | 3 | describe("_isBoolean", function() { 4 | it(`"Should return false if passed anything other than a string.`, function() { 5 | expect(_isBoolean()).toBe(false); 6 | expect(_isBoolean([])).toBe(false); 7 | expect(_isBoolean({})).toBe(false); 8 | expect(_isBoolean(null)).toBe(false); 9 | expect(_isBoolean(undefined)).toBe(false); 10 | expect(_isBoolean(NaN)).toBe(false); 11 | expect(_isBoolean(new Set())).toBe(false); 12 | expect(_isBoolean(new WeakSet())).toBe(false); 13 | expect(_isBoolean(new Map())).toBe(false); 14 | expect(_isBoolean(new WeakMap())).toBe(false); 15 | expect(_isBoolean(document.createElement("div"))).toBe(false); 16 | expect(_isBoolean(15)).toBe(false); 17 | expect(_isBoolean(15.45)).toBe(false); 18 | expect(_isBoolean(Math.PI)).toBe(false); 19 | expect(_isBoolean("")).toBe(false); 20 | expect(_isBoolean("345")).toBe(false); 21 | expect(_isBoolean(new String("window"))).toBe(false); 22 | expect(_isBoolean(new String("😋📋👌"))).toBe(false); 23 | expect(_isBoolean(0)).toBe(false); 24 | expect(_isBoolean(1)).toBe(false); 25 | expect(_isBoolean(true)).toBe(true); 26 | expect(_isBoolean(false)).toBe(true); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/_isPlainObjectSpecs.js: -------------------------------------------------------------------------------- 1 | import _isPlainObject from "../dist/internal/helpers/_isPlainObject.js"; 2 | 3 | class Foo { 4 | constructor(a) { 5 | this.a = 1; 6 | } 7 | } 8 | 9 | function Doo(a) { this.a = 1; } 10 | 11 | describe("_isPlainObject", function() { 12 | it(`"Should return false if passed anything other than a plain object.`, function() { 13 | expect(_isPlainObject()).toBe(false); 14 | expect(_isPlainObject([])).toBe(false); 15 | expect(_isPlainObject({})).toBe(true); 16 | expect(_isPlainObject(null)).toBe(false); 17 | expect(_isPlainObject(undefined)).toBe(false); 18 | expect(_isPlainObject(NaN)).toBe(false); 19 | expect(_isPlainObject(new Set())).toBe(false); 20 | expect(_isPlainObject(new WeakSet())).toBe(false); 21 | expect(_isPlainObject(new Map())).toBe(false); 22 | expect(_isPlainObject(new WeakMap())).toBe(false); 23 | expect(_isPlainObject(document.createElement("div"))).toBe(false); 24 | expect(_isPlainObject(15)).toBe(false); 25 | expect(_isPlainObject(15.45)).toBe(false); 26 | expect(_isPlainObject(Math.PI)).toBe(false); 27 | expect(_isPlainObject("")).toBe(false); 28 | expect(_isPlainObject("345")).toBe(false); 29 | expect(_isPlainObject(new String("window"))).toBe(false); 30 | expect(_isPlainObject(new String("😋📋👌"))).toBe(false); 31 | expect(_isPlainObject(0)).toBe(false); 32 | expect(_isPlainObject(1)).toBe(false); 33 | expect(_isPlainObject(true)).toBe(false); 34 | expect(_isPlainObject(false)).toBe(false); 35 | 36 | expect(_isPlainObject(new Object())).toBe(true); 37 | expect(_isPlainObject({ 'x': 0, 'y': 0 })).toBe(true); 38 | expect(_isPlainObject(Object.create(null))).toBe(true); 39 | expect(_isPlainObject(Object.create(Object.prototype))).toBe(true); 40 | expect(_isPlainObject(Object.prototype)).toBe(true); 41 | expect(_isPlainObject(Object)).toBe(false); 42 | expect(_isPlainObject(Math)).toBe(false); 43 | expect(_isPlainObject(Math.abs)).toBe(false); 44 | expect(_isPlainObject(new Foo())).toBe(false); 45 | expect(_isPlainObject(new Doo())).toBe(false); 46 | expect(_isPlainObject([1, 2, 3])).toBe(false); 47 | expect(_isPlainObject(Object)).toBe(false); 48 | expect(_isPlainObject(null)).toBe(false); 49 | expect(_isPlainObject('str')).toBe(false); 50 | expect(_isPlainObject(5)).toBe(false); 51 | expect(_isPlainObject(true)).toBe(false); 52 | expect(_isPlainObject(undefined)).toBe(false); 53 | expect(_isPlainObject(new Date())).toBe(false); 54 | }); 55 | }); -------------------------------------------------------------------------------- /test/_isStringSpecs.js: -------------------------------------------------------------------------------- 1 | import _isString from "../dist/internal/helpers/_isString.js"; 2 | 3 | describe("_isString", function() { 4 | it(`"Should return false if passed anything other than a string.`, function() { 5 | expect(_isString()).toBe(false); 6 | expect(_isString([])).toBe(false); 7 | expect(_isString({})).toBe(false); 8 | expect(_isString(null)).toBe(false); 9 | expect(_isString(undefined)).toBe(false); 10 | expect(_isString(NaN)).toBe(false); 11 | expect(_isString(new Set())).toBe(false); 12 | expect(_isString(new WeakSet())).toBe(false); 13 | expect(_isString(new Map())).toBe(false); 14 | expect(_isString(new WeakMap())).toBe(false); 15 | expect(_isString(document.createElement("div"))).toBe(false); 16 | expect(_isString(15)).toBe(false); 17 | expect(_isString(15.45)).toBe(false); 18 | expect(_isString(Math.PI)).toBe(false); 19 | expect(_isString("")).toBe(true); 20 | expect(_isString("345")).toBe(true); 21 | expect(_isString(new String("window"))).toBe(true); 22 | expect(_isString(new String("😋📋👌"))).toBe(true); 23 | expect(_isString(0)).toBe(false); 24 | expect(_isString(1)).toBe(false); 25 | expect(_isString(true)).toBe(false); 26 | expect(_isString(false)).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/_isValidBemObjectSpecs.js: -------------------------------------------------------------------------------- 1 | import _isValidBemObject from "../dist/internal/bem/_isValidBemObject.js"; 2 | 3 | describe("_isValidBemObject", function() { 4 | it(`"Should return false if passed anything other than a BEM modifier object.`, function() { 5 | expect(_isValidBemObject(0)).toBe(false); 6 | expect(_isValidBemObject("")).toBe(false); 7 | expect(_isValidBemObject("__ -- -")).toBe(false); 8 | expect(_isValidBemObject([])).toBe(false); 9 | expect(_isValidBemObject()).toBe(false); 10 | 11 | expect(_isValidBemObject({ 12 | "menu": null 13 | })).toBe(false); 14 | 15 | expect(_isValidBemObject({ 16 | "menu": undefined 17 | })).toBe(false); 18 | 19 | expect(_isValidBemObject({ 20 | "menu": 42 21 | })).toBe(false); 22 | 23 | expect(_isValidBemObject({ 24 | "menu": false 25 | })).toBe(false); 26 | 27 | expect(_isValidBemObject({ 28 | "menu": true 29 | })).toBe(false); 30 | 31 | expect(_isValidBemObject({ 32 | "menu": [] 33 | })).toBe(false); 34 | }); 35 | 36 | it(`"Should return true if passed a valid BEM modifier object: with boolean, string or number as properties`, function() { 37 | expect(_isValidBemObject({})).toBe(true); 38 | 39 | expect(_isValidBemObject({ 40 | "menu": {} 41 | })).toBe(true); 42 | 43 | expect(_isValidBemObject({ 44 | "menu": { 45 | "active": true 46 | } 47 | })).toBe(true); 48 | 49 | expect(_isValidBemObject({ 50 | "menu": { 51 | "active": true, 52 | "level": "42", 53 | "test": 24 54 | }, 55 | "button": { 56 | "active": true 57 | } 58 | })).toBe(true); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/_isValidDelimiterArraySpecs.js: -------------------------------------------------------------------------------- 1 | import _isValidDelimiterArray from "../dist/internal/bem/_isValidDelimiterArray.js"; 2 | 3 | describe("_isValidDelimiterArray", function() { 4 | it(`"Should return false if passed anything other than a BEM delimiter array.`, function() { 5 | expect(_isValidDelimiterArray()).toBe(false); 6 | expect(_isValidDelimiterArray(0)).toBe(false); 7 | expect(_isValidDelimiterArray("")).toBe(false); 8 | expect(_isValidDelimiterArray("__ -- -")).toBe(false); 9 | expect(_isValidDelimiterArray({})).toBe(false); 10 | expect(_isValidDelimiterArray([])).toBe(false); 11 | expect(_isValidDelimiterArray(["", "", ""])).toBe(false); 12 | expect(_isValidDelimiterArray(["__", "--"])).toBe(false); 13 | expect(_isValidDelimiterArray(["__", "--", ""])).toBe(false); 14 | expect(_isValidDelimiterArray(["__", "", "-"])).toBe(false); 15 | expect(_isValidDelimiterArray(["", "--", "-"])).toBe(false); 16 | }); 17 | 18 | it(`"Should return true if passed a valid BEM delimiter array: with 3 non-empty strings.`, function() { 19 | expect(_isValidDelimiterArray(["__", "--", "-"])).toBe(true); 20 | expect(_isValidDelimiterArray(["__", "_", "_"])).toBe(true); 21 | expect(_isValidDelimiterArray(["-", "--", "--"])).toBe(true); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/_isValidModifierObjectSpecs.js: -------------------------------------------------------------------------------- 1 | import _isValidModifierObject from "../dist/internal/bem/_isValidModifierObject.js"; 2 | 3 | describe("_isValidModifierObject", function() { 4 | it(`"Should return false if passed anything other than a BEM modifier object.`, function() { 5 | expect(_isValidModifierObject(0)).toBe(false); 6 | expect(_isValidModifierObject("")).toBe(false); 7 | expect(_isValidModifierObject("__ -- -")).toBe(false); 8 | expect(_isValidModifierObject([])).toBe(false); 9 | expect(_isValidModifierObject()).toBe(false); 10 | 11 | expect(_isValidModifierObject({ 12 | "active": null 13 | })).toBe(false); 14 | 15 | expect(_isValidModifierObject({ 16 | "active": undefined 17 | })).toBe(false); 18 | }); 19 | 20 | it(`"Should return true if passed a valid BEM modifier object: with boolean, string or number as properties`, function() { 21 | expect(_isValidModifierObject({})).toBe(true); 22 | 23 | expect(_isValidModifierObject({ 24 | "active": true 25 | })).toBe(true); 26 | 27 | expect(_isValidModifierObject({ 28 | "active": false, 29 | "level": 42 30 | })).toBe(true); 31 | 32 | expect(_isValidModifierObject({ 33 | "active": 42 34 | })).toBe(true); 35 | 36 | expect(_isValidModifierObject({ 37 | "active": "42" 38 | })).toBe(true); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/_removeClassesBeginningWithButNotSpecs.js: -------------------------------------------------------------------------------- 1 | import _removeClassesBeginningWithButNot from "../dist/internal/classname/_removeClassesBeginningWithButNot.js"; 2 | 3 | describe("_removeClassesBeginningWithButNot", function() { 4 | for (let i = 0; i < 5; i++) { 5 | let newEl = document.createElement("div"); 6 | newEl.className = "menu__item button button__active button__level--" + i; 7 | document.body.appendChild(newEl); 8 | } 9 | 10 | it("Should remove one or many CSS classes from a single HTMLElement.", function() { 11 | let singleHTMLElement = document.querySelector(".button__level--1"); 12 | _removeClassesBeginningWithButNot(singleHTMLElement, "button"); 13 | 14 | expect(singleHTMLElement.className).toBe("menu__item button"); 15 | }); 16 | 17 | it("Should not change a CSS class when trying to remove an inexistent class.", function() { 18 | let singleHTMLElement = document.querySelector(".button__level--2"); 19 | _removeClassesBeginningWithButNot(singleHTMLElement, "bogus"); 20 | 21 | expect(singleHTMLElement.className).toBe("menu__item button button__active button__level--2"); 22 | }); 23 | 24 | it("Should remove one or many CSS classes from many HTMLElements.", function() { 25 | let elementsNodeList = document.querySelectorAll(".button"); 26 | 27 | _removeClassesBeginningWithButNot(elementsNodeList, "button"); 28 | 29 | for (let i = 0, len = elementsNodeList.length; i < len; i++) { 30 | expect(elementsNodeList[i].className).toBe("menu__item button"); 31 | } 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/addClassSpecs.js: -------------------------------------------------------------------------------- 1 | import addClass from "../dist/addClass.js"; 2 | 3 | describe("addClass", function() { 4 | for (let i = 0; i < 5; i++) { 5 | let newEl = document.createElement("div"); 6 | newEl.className = "addClass addClass-" + i; 7 | document.body.appendChild(newEl); 8 | } 9 | 10 | it("Should add a CSS class to a single HTMLElement.", function() { 11 | let singleHTMLElement = document.querySelector(".addClass-1"); 12 | addClass(singleHTMLElement, "oneMore"); 13 | 14 | expect(singleHTMLElement.className).toBe("addClass addClass-1 oneMore"); 15 | }); 16 | 17 | it("Should not repeat the addition of the same CSS class.", function() { 18 | let singleHTMLElement = document.querySelector(".addClass-1"); 19 | addClass(singleHTMLElement, "oneMore"); 20 | 21 | expect(singleHTMLElement.className).toBe("addClass addClass-1 oneMore"); 22 | }); 23 | 24 | it("Should add a CSS class to many HTMLElements.", function() { 25 | let elementsNodeList = document.querySelectorAll(".addClass"); 26 | 27 | for (let i = 0, len = elementsNodeList.length; i < len; i++) { 28 | elementsNodeList[i].className = "addClass"; 29 | } 30 | 31 | addClass(elementsNodeList, "oneMore"); 32 | 33 | for (let i = 0, len = elementsNodeList.length; i < len; i++) { 34 | expect(elementsNodeList[i].className).toBe("addClass oneMore"); 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/afterFirstSpecs.js: -------------------------------------------------------------------------------- 1 | import afterFirst from "../dist/afterFirst.js"; 2 | 3 | describe("afterFirst", function() { 4 | it(`Should find "rallelepiped" after the first "a" in "Parallelepiped".`, function() { 5 | expect( 6 | afterFirst("Parallelepiped", "a") 7 | ).toBe("rallelepiped"); 8 | }); 9 | 10 | it(`Should find "lepiped" after the first "e" in "Parallelepiped".`, function() { 11 | expect( 12 | afterFirst("Parallelepiped", "e") 13 | ).toBe("lepiped"); 14 | }); 15 | 16 | it(`"Should find "lepiped" after the first "le" in "Parallelepiped".`, function() { 17 | expect( 18 | afterFirst("Parallelepiped", "le") 19 | ).toBe("lepiped"); 20 | }); 21 | 22 | it(`"Should find "arallelepiped" after the first "P" in "Parallelepiped".`, function() { 23 | expect( 24 | afterFirst("Parallelepiped", "P") 25 | ).toBe("arallelepiped"); 26 | }); 27 | 28 | it(`"Should find "👌" after the first "📋" in "😋📋👌".`, function() { 29 | expect( 30 | afterFirst("😋📋👌", "📋") 31 | ).toBe("👌"); 32 | }); 33 | 34 | it(`"Should not find anything after the first "x" in "Parallelepiped".`, function() { 35 | expect( 36 | afterFirst("Parallelepiped", "x") 37 | ).toBe(undefined); 38 | }); 39 | 40 | it(`"Should not find anything if the delimiter is ommited.`, function() { 41 | expect( 42 | afterFirst("Parallelepiped") 43 | ).toBe(undefined); 44 | }); 45 | 46 | it(`Should find "Parallelepiped" after the first "" in "Parallelepiped".`, function() { 47 | expect( 48 | afterFirst("Parallelepiped", "") 49 | ).toBe("Parallelepiped"); 50 | }); 51 | 52 | it(`Should throw an error if the first parameter is not a string.`, function() { 53 | expect( 54 | () => afterFirst(undefined, "") 55 | ).toThrow(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/afterLastSpecs.js: -------------------------------------------------------------------------------- 1 | import afterLast from "../dist/afterLast.js"; 2 | 3 | describe("afterLast", function() { 4 | it(`Should find "llelepiped" after the last "a" in "Parallelepiped".`, function() { 5 | expect( 6 | afterLast("Parallelepiped", "a") 7 | ).toBe("llelepiped"); 8 | }); 9 | 10 | it(`Should find "d" after the last "e" in "Parallelepiped".`, function() { 11 | expect( 12 | afterLast("Parallelepiped", "e") 13 | ).toBe("d"); 14 | }); 15 | 16 | it(`"Should find "piped" after the last "le" in "Parallelepiped".`, function() { 17 | expect( 18 | afterLast("Parallelepiped", "le") 19 | ).toBe("piped"); 20 | }); 21 | 22 | it(`"Should find "arallelepiped" after the last "P" in "Parallelepiped".`, function() { 23 | expect( 24 | afterLast("Parallelepiped", "P") 25 | ).toBe("arallelepiped"); 26 | }); 27 | 28 | it(`"Should find "👌" after the last "📋" in "😋📋👌".`, function() { 29 | expect( 30 | afterLast("😋📋👌", "📋") 31 | ).toBe("👌"); 32 | }); 33 | 34 | it(`"Should not find anything after the last "x" in "Parallelepiped".`, function() { 35 | expect( 36 | afterLast("Parallelepiped", "x") 37 | ).toBe(undefined); 38 | }); 39 | 40 | it(`"Should not find anything if the delimiter is ommited.`, function() { 41 | expect( 42 | afterLast("Parallelepiped") 43 | ).toBe(undefined); 44 | }); 45 | 46 | it(`Should find "" after the last "" in "Parallelepiped".`, function() { 47 | expect( 48 | afterLast("Parallelepiped", "") 49 | ).toBe(""); 50 | }); 51 | 52 | it(`Should throw an error if the first parameter is not a string.`, function() { 53 | expect( 54 | () => afterLast(undefined, "") 55 | ).toThrow(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/beforeFirstSpecs.js: -------------------------------------------------------------------------------- 1 | import beforeFirst from "../dist/beforeFirst.js"; 2 | 3 | describe("beforeFirst", function() { 4 | it(`Should find "P" before the first "a" in "Parallelepiped".`, function() { 5 | expect( 6 | beforeFirst("Parallelepiped", "a") 7 | ).toBe("P"); 8 | }); 9 | 10 | it(`Should find "Parall" before the first "e" in "Parallelepiped".`, function() { 11 | expect( 12 | beforeFirst("Parallelepiped", "e") 13 | ).toBe("Parall"); 14 | }); 15 | 16 | it(`"Should find "Paral" before the first "le" in "Parallelepiped".`, function() { 17 | expect( 18 | beforeFirst("Parallelepiped", "le") 19 | ).toBe("Paral"); 20 | }); 21 | 22 | it(`"Should not find anything before the first "P" in "Parallelepiped".`, function() { 23 | expect( 24 | beforeFirst("Parallelepiped", "P") 25 | ).toBe(""); 26 | }); 27 | 28 | it(`"Should find "😋" before the first "📋" in "😋📋👌".`, function() { 29 | expect( 30 | beforeFirst("😋📋👌", "📋") 31 | ).toBe("😋"); 32 | }); 33 | 34 | it(`"Should not find anything before the first "x" in "Parallelepiped".`, function() { 35 | expect( 36 | beforeFirst("Parallelepiped", "x") 37 | ).toBe(undefined); 38 | }); 39 | 40 | it(`"Should not find anything if the delimiter is ommited.`, function() { 41 | expect( 42 | beforeFirst("Parallelepiped") 43 | ).toBe(undefined); 44 | }); 45 | 46 | it(`Should find "" before the first "" in "Parallelepiped".`, function() { 47 | expect( 48 | beforeFirst("Parallelepiped", "") 49 | ).toBe(""); 50 | }); 51 | 52 | it(`Should throw an error if the first parameter is not a string.`, function() { 53 | expect( 54 | () => beforeFirst(undefined, "") 55 | ).toThrow(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/beforeLastSpecs.js: -------------------------------------------------------------------------------- 1 | import beforeLast from "../dist/beforeLast.js"; 2 | 3 | describe("beforeLast", function() { 4 | it(`Should find "Par" before the last "a" in "Parallelepiped".`, function() { 5 | expect( 6 | beforeLast("Parallelepiped", "a") 7 | ).toBe("Par"); 8 | }); 9 | 10 | it(`Should find "Parallelepip" before the last "e" in "Parallelepiped".`, function() { 11 | expect( 12 | beforeLast("Parallelepiped", "e") 13 | ).toBe("Parallelepip"); 14 | }); 15 | 16 | it(`"Should find "Paralle" before the last "le" in "Parallelepiped".`, function() { 17 | expect( 18 | beforeLast("Parallelepiped", "le") 19 | ).toBe("Paralle"); 20 | }); 21 | 22 | it(`"Should not find anything before the last "P" in "Parallelepiped".`, function() { 23 | expect( 24 | beforeLast("Parallelepiped", "P") 25 | ).toBe(""); 26 | }); 27 | 28 | it(`"Should find "😋" before the last "📋" in "😋📋👌".`, function() { 29 | expect( 30 | beforeLast("😋📋👌", "📋") 31 | ).toBe("😋"); 32 | }); 33 | 34 | it(`"Should not find anything before the last "x" in "Parallelepiped".`, function() { 35 | expect( 36 | beforeLast("Parallelepiped", "x") 37 | ).toBe(undefined); 38 | }); 39 | 40 | it(`"Should not find anything if the delimiter is ommited.`, function() { 41 | expect( 42 | beforeLast("Parallelepiped") 43 | ).toBe(undefined); 44 | }); 45 | 46 | it(`Should find "Parallelepiped" before the last "" in "Parallelepiped".`, function() { 47 | expect( 48 | beforeLast("Parallelepiped", "") 49 | ).toBe("Parallelepiped"); 50 | }); 51 | 52 | it(`Should throw an error if the first parameter is not a string.`, function() { 53 | expect( 54 | () => beforeLast(undefined, "") 55 | ).toThrow(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/byAlphabeticalOrderSpecs.js: -------------------------------------------------------------------------------- 1 | import byAlphabeticalOrder from "../dist/byAlphabeticalOrder.js"; 2 | 3 | describe("byAlphabeticalOrder", function() { 4 | it("Should order an array by alphabetical order.", function() { 5 | expect( 6 | ["Athos", "Porthos", "Aramis"].sort(byAlphabeticalOrder()) 7 | ).toEqual(["Aramis", "Athos", "Porthos"]); 8 | }); 9 | 10 | it("Should function without parentheses.", function() { 11 | expect( 12 | ["Athos", "Porthos", "Aramis"].sort(byAlphabeticalOrder) 13 | ).toEqual(["Aramis", "Athos", "Porthos"]); 14 | }); 15 | 16 | it("Should function with accented latin characters usign native localeCompare.", function() { 17 | expect( 18 | ["Áureo", "Árvore", "Ética", "Elástico", "Arte", "Entusiasta"].sort(byAlphabeticalOrder) 19 | ).toEqual(["Arte", "Árvore", "Áureo", "Elástico", "Entusiasta", "Ética"]); 20 | }); 21 | 22 | it("Should function with accented latin characters (upper or lower case) usign native localeCompare.", function() { 23 | expect( 24 | ["áureo", "Árvore", "Ética", "Elástico", "arte", "entusiasta"].sort(byAlphabeticalOrder) 25 | ).toEqual(["arte", "Árvore", "áureo", "Elástico", "entusiasta", "Ética"]); 26 | }); 27 | 28 | it("Should not fail with an array with a single element.", function() { 29 | expect( 30 | ["laser"].sort(byAlphabeticalOrder) 31 | ).toEqual(["laser"]); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/deepGroupBySpecs.js: -------------------------------------------------------------------------------- 1 | import deepGroupBy from "../dist/deepGroupBy.js"; 2 | 3 | describe("deepGroupBy", function() { 4 | const stores = [{ 5 | name: "Iguatemi", 6 | city: "Campinas", 7 | state: "SP" 8 | }, { 9 | name: "Jardins", 10 | city: "São Paulo", 11 | state: "SP" 12 | }, { 13 | name: "Iguatemi", 14 | city: "São Paulo", 15 | state: "SP" 16 | }, { 17 | name: "Pedras", 18 | city: "Búzios", 19 | state: "RJ" 20 | }, { 21 | name: "Ipanema", 22 | city: "Rio de Janeiro", 23 | state: "RJ" 24 | }, { 25 | name: "Leblon", 26 | city: "Rio de Janeiro", 27 | state: "RJ" 28 | }, { 29 | name: "ParkShopping", 30 | city: "Brasília", 31 | state: "DF" 32 | }]; 33 | 34 | const getNamesFirstCharacter = item => item.name.slice(0, 1); 35 | const getCityName = item => item.city; 36 | const getStateName = item => item.state; 37 | 38 | it("Should group stores by the first letter of their names.", function() { 39 | expect(deepGroupBy(stores, getNamesFirstCharacter)).toEqual({ 40 | "I": [{ 41 | name: "Iguatemi", 42 | city: "Campinas", 43 | state: "SP" 44 | }, { 45 | name: "Iguatemi", 46 | city: "São Paulo", 47 | state: "SP" 48 | }, { 49 | name: "Ipanema", 50 | city: "Rio de Janeiro", 51 | state: "RJ" 52 | }], 53 | "J": [{ 54 | name: "Jardins", 55 | city: "São Paulo", 56 | state: "SP" 57 | }], 58 | "P": [{ 59 | name: "Pedras", 60 | city: "Búzios", 61 | state: "RJ" 62 | }, { 63 | name: "ParkShopping", 64 | city: "Brasília", 65 | state: "DF" 66 | }], 67 | "L": [{ 68 | name: "Leblon", 69 | city: "Rio de Janeiro", 70 | state: "RJ" 71 | }] 72 | }); 73 | }); 74 | 75 | it("Should group stores by state and city.", function() { 76 | expect(deepGroupBy(stores, getStateName, getCityName)).toEqual({ 77 | "SP": { 78 | "Campinas": [{ 79 | name: "Iguatemi", 80 | city: "Campinas", 81 | state: "SP" 82 | }], 83 | "São Paulo": [{ 84 | name: "Jardins", 85 | city: "São Paulo", 86 | state: "SP" 87 | }, { 88 | name: "Iguatemi", 89 | city: "São Paulo", 90 | state: "SP" 91 | }] 92 | }, 93 | "RJ": { 94 | "Búzios": [{ 95 | name: "Pedras", 96 | city: "Búzios", 97 | state: "RJ" 98 | }], 99 | "Rio de Janeiro": [{ 100 | name: "Ipanema", 101 | city: "Rio de Janeiro", 102 | state: "RJ" 103 | }, { 104 | name: "Leblon", 105 | city: "Rio de Janeiro", 106 | state: "RJ" 107 | }] 108 | }, 109 | "DF": { 110 | "Brasília": [{ 111 | name: "ParkShopping", 112 | city: "Brasília", 113 | state: "DF" 114 | }] 115 | } 116 | }); 117 | }); 118 | 119 | it("Should group stores by the first letter of their names and the name of the city.", function() { 120 | expect(deepGroupBy(stores, getNamesFirstCharacter, getCityName)).toEqual({ 121 | "I": { 122 | "Campinas": [{ 123 | name: "Iguatemi", 124 | city: "Campinas", 125 | state: "SP" 126 | }], 127 | "São Paulo": [{ 128 | name: "Iguatemi", 129 | city: "São Paulo", 130 | state: "SP" 131 | }], 132 | "Rio de Janeiro": [{ 133 | name: "Ipanema", 134 | city: "Rio de Janeiro", 135 | state: "RJ" 136 | }] 137 | }, 138 | "J": { 139 | "São Paulo": [{ 140 | name: "Jardins", 141 | city: "São Paulo", 142 | state: "SP" 143 | }] 144 | }, 145 | "P": { 146 | "Búzios": [{ 147 | name: "Pedras", 148 | city: "Búzios", 149 | state: "RJ" 150 | }], 151 | "Brasília": [{ 152 | name: "ParkShopping", 153 | city: "Brasília", 154 | state: "DF" 155 | }] 156 | }, 157 | "L": { 158 | "Rio de Janeiro": [{ 159 | name: "Leblon", 160 | city: "Rio de Janeiro", 161 | state: "RJ" 162 | }] 163 | } 164 | }); 165 | }); 166 | }); 167 | -------------------------------------------------------------------------------- /test/getAttrSpecs.js: -------------------------------------------------------------------------------- 1 | import _createDomElement from "../dist/internal/dom/_createDomElement.js"; 2 | import getAttr from "../dist/getAttr.js"; 3 | 4 | describe("getAttr", function() { 5 | it("Should return a boolean attribute from a DOM element written in HTML5 syntax.", function() { 6 | let inputElement = _createDomElement(''); 7 | expect(getAttr(inputElement, "checked")).toBe(true); 8 | }); 9 | 10 | it("Should return a boolean attribute from a DOM element written in HTML4 syntax.", function() { 11 | let inputElement = _createDomElement(''); 12 | expect(getAttr(inputElement, "checked")).toBe(true); 13 | }); 14 | 15 | it("Should return an attribute from a DOM element.", function() { 16 | let linkElement = _createDomElement(''); 17 | expect(getAttr(linkElement, "href")).toBe("#test"); 18 | }); 19 | 20 | it("Should return true for an attribute without a value.", function() { 21 | let videoElement = _createDomElement(''); 22 | expect(getAttr(videoElement, "controls")).toBe(true); 23 | }); 24 | 25 | it("Should return false for a non-existing attribute.", function() { 26 | let videoElement = _createDomElement(''); 27 | expect(getAttr(videoElement, "muted")).toBe(false); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/getDistanceBetweenCoordsSpecs.js: -------------------------------------------------------------------------------- 1 | import getDistanceBetweenCoords from "../dist/getDistanceBetweenCoords.js"; 2 | 3 | describe("getDistanceBetweenCoords", function() { 4 | const errorMsg = "Two arrays of numbers with the same length, representing cartesian coordinates, are expected as parameters."; 5 | 6 | it("Should calculate de distance between two points on a surface.", function() { 7 | expect(getDistanceBetweenCoords([2, 1], [5, 5])).toBe(5); 8 | }); 9 | 10 | it("Should calculate de distance between two points on a surface.", function() { 11 | expect(getDistanceBetweenCoords([0, 0], [3, 4])).toBe(5); 12 | }); 13 | 14 | it("Should calculate de distance between two points on a 3D space.", function() { 15 | expect(getDistanceBetweenCoords([2, 1, 8], [5, 5, 8])).toBe(5); 16 | }); 17 | 18 | it("Should calculate de distance between two points on a 3D space.", function() { 19 | expect(getDistanceBetweenCoords([2, 1, 8], [5, 5, 0])).toBe(Math.sqrt(89)); 20 | }); 21 | 22 | it("Should calculate de distance between two points on a 3D space.", function() { 23 | expect(getDistanceBetweenCoords([2, 1, 8, 0], [5, 5, 8, 2])).toBe(Math.sqrt(29)); 24 | }); 25 | 26 | it("Should calculate de distance between two points in a line.", function() { 27 | expect(getDistanceBetweenCoords([4], [8])).toBe(4); 28 | }); 29 | 30 | it("Should calculate de distance between two points in a line.", function() { 31 | expect(getDistanceBetweenCoords([2], [5])).toBe(3); 32 | }); 33 | 34 | it("Should calculate de distance between two points in a line.", function() { 35 | expect(getDistanceBetweenCoords([-2], [-5])).toBe(3); 36 | }); 37 | 38 | it("Should ignore extra parameters, as only two are expected.", function() { 39 | expect(getDistanceBetweenCoords([-2], [-5], [1001])).toBe(3); 40 | }); 41 | 42 | it("Should throw an error if less than two coordinates are passed as parameters.", function() { 43 | expect(() => getDistanceBetweenCoords()).toThrow(); 44 | }); 45 | 46 | it("Should throw an error if less than two coordinates are passed as parameters.", function() { 47 | expect(() => getDistanceBetweenCoords([2, 1])).toThrow(); 48 | }); 49 | 50 | it("Should throw an error if any of the coordinates is not an array.", function() { 51 | expect(() => getDistanceBetweenCoords([-2, 5], -5)).toThrow(); 52 | }); 53 | 54 | it("Should throw an error if any of the coordinates is not an array.", function() { 55 | expect(() => getDistanceBetweenCoords(-5, [2, 5])).toThrow(); 56 | }); 57 | 58 | it("Should throw an error if coordinates do not have the same length.", function() { 59 | expect(() => getDistanceBetweenCoords([2, 1], [5, 3, 8])).toThrow(); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/getEventPathSpecs.js: -------------------------------------------------------------------------------- 1 | import getEventPath from "../dist/getEventPath.js"; 2 | import trigger from "../dist/trigger.js"; 3 | 4 | describe("getEventPath", function() { 5 | it(`Should return an array with all DOM elements affected by an event.`, function(done) { 6 | let domChild = document.createElement("div"), 7 | domParent = document.createElement("div"), 8 | domGrandparent = document.createElement("div"), 9 | body = document.body, 10 | html = document.querySelector("html"); 11 | 12 | domParent.appendChild(domChild); 13 | domGrandparent.appendChild(domParent); 14 | body.appendChild(domGrandparent); 15 | 16 | const dealWithChildEvent = evt => { 17 | expect(getEventPath(evt)).toEqual([ 18 | domChild, 19 | domParent, 20 | domGrandparent, 21 | body, 22 | html, 23 | document, 24 | window 25 | ]); 26 | }; 27 | 28 | const dealWithGrandparentEvent = evt => { 29 | expect(getEventPath(evt)).toEqual([ 30 | domGrandparent, 31 | body, 32 | html, 33 | document, 34 | window 35 | ]); 36 | 37 | done(); 38 | }; 39 | 40 | domChild.addEventListener("test", dealWithChildEvent); 41 | domGrandparent.addEventListener("test", dealWithGrandparentEvent); 42 | 43 | trigger(domChild, "test"); 44 | trigger(domGrandparent, "test"); 45 | }); 46 | 47 | it(`Should always end with window.`, function(done) { 48 | const dealWithWindowEvent = evt => { 49 | expect(getEventPath(evt)).toEqual([window]); 50 | done(); 51 | }; 52 | 53 | window.addEventListener("test", dealWithWindowEvent); 54 | trigger(window, "test"); 55 | }); 56 | 57 | it(`Should return undefined when trying to run getEventPath on something other than an event.`, function() { 58 | expect(getEventPath(document.createElement("div"))).toBe(undefined); 59 | expect(getEventPath("laser")).toBe(undefined); 60 | expect(getEventPath(0)).toBe(undefined); 61 | expect(getEventPath(document.createTextNode("laser"))).toBe(undefined); 62 | expect(getEventPath(document.querySelectorAll("div"))).toBe(undefined); 63 | expect(getEventPath(window)).toBe(undefined); 64 | expect(getEventPath(document)).toBe(undefined); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/hasClassSpecs.js: -------------------------------------------------------------------------------- 1 | import hasClass from "../dist/hasClass.js"; 2 | 3 | describe("hasClass", function() { 4 | it("Should verify if an HTMLElement has a CSS class.", function() { 5 | let oneElement = document.createElement("a"); 6 | oneElement.className = "link reference"; 7 | 8 | expect(hasClass(oneElement, "link")).toBe(true); 9 | expect(hasClass(oneElement, "button")).toBe(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/listenSpecs.js: -------------------------------------------------------------------------------- 1 | import listen from "../dist/listen.js"; 2 | import trigger from "../dist/trigger.js"; 3 | 4 | describe("listen", function() { 5 | it("Should allow document as parameter.", function() { 6 | let eventCallback = jasmine.createSpy("eventCallback"); 7 | expect(() => listen(document, "open", eventCallback)).not.toThrow(); 8 | }); 9 | 10 | it("Should allow window as parameter.", function() { 11 | let eventCallback = jasmine.createSpy("eventCallback"); 12 | expect(() => listen(window, "open", eventCallback)).not.toThrow(); 13 | }); 14 | 15 | it("Should allow many dom elements, including document and window, as parameter.", function() { 16 | let eventCallback = jasmine.createSpy("eventCallback"), 17 | singleDiv = document.createElement("div"); 18 | 19 | expect(() => listen([singleDiv, document, window], "open", eventCallback)).not.toThrow(); 20 | }); 21 | 22 | it("Should listen for a single event of a single DOM element.", function(done) { 23 | let eventCallback = jasmine.createSpy("eventCallback"), 24 | singleDiv = document.createElement("div"); 25 | 26 | document.body.appendChild(singleDiv); 27 | 28 | listen(singleDiv, "open", eventCallback); 29 | 30 | trigger(singleDiv, "open", false); 31 | 32 | setTimeout(() => { 33 | expect(eventCallback.calls.count()).toBe(1); 34 | done(); 35 | }, 20); 36 | }); 37 | 38 | it("Should listen for multiple events of a single DOM element.", function(done) { 39 | let eventCallback = jasmine.createSpy("eventCallback"), 40 | singleDiv = document.createElement("div"); 41 | 42 | document.body.appendChild(singleDiv); 43 | 44 | listen(singleDiv, "one two three", eventCallback); 45 | 46 | trigger(singleDiv, "one", false); 47 | trigger(singleDiv, "two", false); 48 | trigger(singleDiv, "three", false); 49 | 50 | setTimeout(() => { 51 | expect(eventCallback.calls.count()).toBe(3); 52 | done(); 53 | }, 20); 54 | }); 55 | 56 | it("Should listen for multiple events of multiple DOM elements.", function(done) { 57 | let eventCallback = jasmine.createSpy("eventCallback"), 58 | multipleDivs = [document.createElement("div"), document.createElement("div")]; 59 | 60 | document.body.appendChild(multipleDivs[0]); 61 | document.body.appendChild(multipleDivs[1]); 62 | 63 | listen(multipleDivs, "one two three", eventCallback); 64 | 65 | trigger(multipleDivs[0], "one", false); 66 | trigger(multipleDivs[0], "two", false); 67 | trigger(multipleDivs[0], "three", false); 68 | trigger(multipleDivs[1], "one", false); 69 | trigger(multipleDivs[1], "two", false); 70 | trigger(multipleDivs[1], "three", false); 71 | 72 | setTimeout(() => { 73 | expect(eventCallback.calls.count()).toBe(6); 74 | done(); 75 | }, 20); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/oneOutOfSpecs.js: -------------------------------------------------------------------------------- 1 | import oneOutOf from "../dist/oneOutOf.js"; 2 | 3 | function testRandomness(num) { 4 | let expectedPositive = 1 / num; 5 | let expectedNegative = 1 - expectedPositive; 6 | let expectedRatio = expectedPositive / expectedNegative; 7 | let expectedRatioPercent = 100 * expectedRatio; 8 | 9 | let positives = 0; 10 | let negatives = 0; 11 | 12 | for (let i = 0; i < 1000000; i++) { 13 | if (oneOutOf(num)) { 14 | positives++; 15 | } 16 | else { 17 | negatives++; 18 | } 19 | } 20 | 21 | let actualRatio = positives / negatives; 22 | 23 | return it(`Should return true approximately ${expectedRatioPercent}% of times with oneOutOf(${num}).`, function() { 24 | expect(actualRatio < (expectedRatio + 0.01) && actualRatio > (expectedRatio - 0.01)).toBe(true); 25 | }); 26 | } 27 | 28 | describe("oneOutOf", function() { 29 | testRandomness(2); 30 | testRandomness(5); 31 | testRandomness(10); 32 | testRandomness(25); 33 | testRandomness(100); 34 | testRandomness(250000); 35 | 36 | it("Should return true when 1 is used as parameter.", function() { 37 | expect(oneOutOf(1)).toBe(true); 38 | }); 39 | 40 | it("Should throw an error is the parameter is not a number greater than 1.", function() { 41 | expect(() => oneOutOf()).toThrow(); 42 | expect(() => oneOutOf(0)).toThrow(); 43 | expect(() => oneOutOf(-45)).toThrow(); 44 | expect(() => oneOutOf("25")).toThrow(); 45 | expect(() => oneOutOf([])).toThrow(); 46 | expect(() => oneOutOf({})).toThrow(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/parentsSpecs.js: -------------------------------------------------------------------------------- 1 | import parents from "../dist/parents.js"; 2 | 3 | describe("parents", function() { 4 | it(`Should return all parents of a DOM element.`, function() { 5 | var domChild = document.createElement("div"); 6 | expect(parents(domChild)).toEqual([]); 7 | 8 | var domParent = document.createElement("div"); 9 | domParent.appendChild(domChild); 10 | expect(parents(domChild)).toEqual([ 11 | domParent 12 | ]); 13 | 14 | var domGrandparent = document.createElement("div"); 15 | domGrandparent.appendChild(domParent); 16 | expect(parents(domChild)).toEqual([ 17 | domParent, 18 | domGrandparent 19 | ]); 20 | 21 | document.body.appendChild(domGrandparent); 22 | expect(parents(domChild)).toEqual([ 23 | domParent, 24 | domGrandparent, 25 | document.body, 26 | document.querySelector("html"), 27 | document 28 | ]); 29 | }); 30 | 31 | it(`Should throw an error when trying to find parents of something other than a DOM element.`, function() { 32 | expect(() => parents("laser")).toThrow(); 33 | expect(() => parents(0)).toThrow(); 34 | expect(() => parents(document.createTextNode("laser"))).toThrow(); 35 | expect(() => parents(document.querySelectorAll("div"))).toThrow(); 36 | }); 37 | 38 | it(`Should return empty arrays for document and window.`, function() { 39 | expect(parents(window)).toEqual([]); 40 | expect(parents(document)).toEqual([]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/removeAttrSpecs.js: -------------------------------------------------------------------------------- 1 | import _createDomElement from "../dist/internal/dom/_createDomElement.js"; 2 | import removeAttr from "../dist/removeAttr.js"; 3 | 4 | describe("removeAttr", function() { 5 | it("Should remove an attribute from a DOM element.", function() { 6 | let videoHTMLElement = document.createElement("video"); 7 | videoHTMLElement.setAttribute("controls", ""); 8 | videoHTMLElement.setAttribute("data-paused", ""); 9 | 10 | removeAttr(videoHTMLElement, "controls"); 11 | removeAttr(videoHTMLElement, "data-paused"); 12 | 13 | expect(videoHTMLElement.hasAttribute("controls")).toBe(false); 14 | expect(videoHTMLElement.getAttribute("controls")).toBe(null); 15 | 16 | expect(videoHTMLElement.hasAttribute("data-paused")).toBe(false); 17 | expect(videoHTMLElement.getAttribute("controls")).toBe(null); 18 | expect(videoHTMLElement.dataset.paused).toBe(undefined); 19 | }); 20 | 21 | it("Should remove an attribute from a DOM element.", function() { 22 | let oneElement = _createDomElement('Level 42'); 23 | removeAttr(oneElement, "data-level"); 24 | 25 | expect(oneElement.getAttribute("data-level")).toBe(null); 26 | expect(oneElement.dataset.level).toBe(undefined); 27 | }); 28 | 29 | it("Should remove an attribute from a DOM element.", function() { 30 | let oneElement = _createDomElement('News'); 31 | removeAttr(oneElement, "class"); 32 | 33 | expect(oneElement.getAttribute("class")).toBe(null); 34 | expect(oneElement.className).toBe(""); 35 | }); 36 | 37 | it("Should accept a group of DOM nodes.", function() { 38 | let domElA = document.createElement("div"), 39 | domElB = document.createElement("div"); 40 | 41 | domElA.className = "removeAttr"; 42 | domElA.dataset.level = 42; 43 | domElB.className = "removeAttr"; 44 | domElB.dataset.level = 42; 45 | 46 | document.body.appendChild(domElA); 47 | document.body.appendChild(domElB); 48 | 49 | let manyElements = document.querySelectorAll(".removeAttr"); 50 | 51 | removeAttr(manyElements, "data-level"); 52 | 53 | expect(manyElements[0].getAttribute("data-level")).toBe(null); 54 | expect(manyElements[1].getAttribute("data-level")).toBe(null); 55 | expect(manyElements[0].dataset.level).toBe(undefined); 56 | expect(manyElements[1].dataset.level).toBe(undefined); 57 | }); 58 | 59 | it("Should accept a group of DOM nodes.", function() { 60 | let listElement = _createDomElement('AB'), 61 | manyElements = listElement.querySelectorAll("li"); 62 | 63 | removeAttr(manyElements, "class"); 64 | 65 | expect(manyElements[0].getAttribute("class")).toBe(null); 66 | expect(manyElements[1].getAttribute("class")).toBe(null); 67 | expect(manyElements[0].className).toBe(""); 68 | expect(manyElements[1].className).toBe(""); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/removeAttrsSpecs.js: -------------------------------------------------------------------------------- 1 | import _createDomElement from "../dist/internal/dom/_createDomElement.js"; 2 | import removeAttrs from "../dist/removeAttrs.js"; 3 | 4 | describe("removeAttrs", function() { 5 | it("Should remove many attributes from a DOM element.", function() { 6 | let videoHTMLElement = document.createElement("video"); 7 | videoHTMLElement.setAttribute("controls", ""); 8 | videoHTMLElement.setAttribute("data-paused", ""); 9 | 10 | removeAttrs(videoHTMLElement, ["controls", "data-paused"]); 11 | 12 | expect(videoHTMLElement.hasAttribute("controls")).toBe(false); 13 | expect(videoHTMLElement.getAttribute("controls")).toBe(null); 14 | 15 | expect(videoHTMLElement.hasAttribute("data-paused")).toBe(false); 16 | expect(videoHTMLElement.getAttribute("controls")).toBe(null); 17 | expect(videoHTMLElement.dataset.paused).toBe(undefined); 18 | }); 19 | 20 | it("Should remove many attributes from a DOM element.", function() { 21 | let oneElement = _createDomElement('Level 42'); 22 | removeAttrs(oneElement, ["data-level", "class"]); 23 | 24 | expect(oneElement.getAttribute("data-level")).toBe(null); 25 | expect(oneElement.getAttribute("class")).toBe(null); 26 | expect(oneElement.dataset.level).toBe(undefined); 27 | expect(oneElement.className).toBe(""); 28 | }); 29 | 30 | it("Should accept a group of DOM nodes.", function() { 31 | let domElA = document.createElement("div"), 32 | domElB = document.createElement("div"); 33 | 34 | domElA.className = "removeAttr"; 35 | domElA.dataset.level = 42; 36 | domElB.className = "removeAttr"; 37 | domElB.dataset.level = 42; 38 | 39 | document.body.appendChild(domElA); 40 | document.body.appendChild(domElB); 41 | 42 | let manyElements = document.querySelectorAll(".removeAttr"); 43 | 44 | removeAttrs(manyElements, ["data-level", "class"]); 45 | 46 | expect(manyElements[0].getAttribute("data-level")).toBe(null); 47 | expect(manyElements[0].getAttribute("class")).toBe(null); 48 | expect(manyElements[0].dataset.level).toBe(undefined); 49 | expect(manyElements[0].className).toBe(""); 50 | }); 51 | 52 | it("Should accept a group of DOM nodes.", function() { 53 | let listElement = _createDomElement('AB'), 54 | manyElements = listElement.querySelectorAll("li"); 55 | 56 | removeAttrs(manyElements, ["data-level", "class"]); 57 | 58 | expect(manyElements[0].getAttribute("data-level")).toBe(null); 59 | expect(manyElements[1].getAttribute("data-level")).toBe(null); 60 | expect(manyElements[0].getAttribute("class")).toBe(null); 61 | expect(manyElements[1].getAttribute("class")).toBe(null); 62 | expect(manyElements[0].dataset.level).toBe(undefined); 63 | expect(manyElements[1].dataset.level).toBe(undefined); 64 | expect(manyElements[0].className).toBe(""); 65 | expect(manyElements[1].className).toBe(""); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/removeClassSpecs.js: -------------------------------------------------------------------------------- 1 | import removeClass from "../dist/removeClass.js"; 2 | 3 | describe("removeClass", function() { 4 | for (let i = 0; i < 5; i++) { 5 | let newEl = document.createElement("div"); 6 | newEl.className = "oneMore removeClass removeClass-" + i; 7 | document.body.appendChild(newEl); 8 | } 9 | 10 | it("Should add a CSS class to a single HTMLElement.", function() { 11 | let singleHTMLElement = document.querySelector(".removeClass-1"); 12 | removeClass(singleHTMLElement, "oneMore"); 13 | 14 | expect(singleHTMLElement.className).toBe("removeClass removeClass-1"); 15 | }); 16 | 17 | it("Should not change a CSS class when trying to remove an inexistent class.", function() { 18 | let singleHTMLElement = document.querySelector(".removeClass-1"); 19 | removeClass(singleHTMLElement, "oneMore"); 20 | 21 | expect(singleHTMLElement.className).toBe("removeClass removeClass-1"); 22 | }); 23 | 24 | it("Should add a CSS class to many HTMLElements.", function() { 25 | let elementsNodeList = document.querySelectorAll(".removeClass"); 26 | 27 | for (let i = 0, len = elementsNodeList.length; i < len; i++) { 28 | elementsNodeList[i].className = "oneMore removeClass"; 29 | } 30 | 31 | removeClass(elementsNodeList, "oneMore"); 32 | 33 | for (let i = 0, len = elementsNodeList.length; i < len; i++) { 34 | expect(elementsNodeList[i].className).toBe("removeClass"); 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/selfAndParentsSpecs.js: -------------------------------------------------------------------------------- 1 | import selfAndParents from "../dist/selfAndParents.js"; 2 | 3 | describe("selfAndParents", function() { 4 | it(`Should return all selfAndParents of a DOM element.`, function() { 5 | var domChild = document.createElement("div"); 6 | expect(selfAndParents(domChild)).toEqual([ 7 | domChild 8 | ]); 9 | 10 | var domParent = document.createElement("div"); 11 | domParent.appendChild(domChild); 12 | expect(selfAndParents(domChild)).toEqual([ 13 | domChild, 14 | domParent 15 | ]); 16 | 17 | var domGrandparent = document.createElement("div"); 18 | domGrandparent.appendChild(domParent); 19 | expect(selfAndParents(domChild)).toEqual([ 20 | domChild, 21 | domParent, 22 | domGrandparent 23 | ]); 24 | 25 | document.body.appendChild(domGrandparent); 26 | expect(selfAndParents(domChild)).toEqual([ 27 | domChild, 28 | domParent, 29 | domGrandparent, 30 | document.body, 31 | document.querySelector("html"), 32 | document 33 | ]); 34 | }); 35 | 36 | it(`Should throw an error when trying to find selfAndParents of something other than a DOM element.`, function() { 37 | expect(() => selfAndParents("laser")).toThrow(); 38 | expect(() => selfAndParents(0)).toThrow(); 39 | expect(() => selfAndParents(document.createTextNode("laser"))).toThrow(); 40 | expect(() => selfAndParents(document.querySelectorAll("div"))).toThrow(); 41 | }); 42 | 43 | it(`Should return an array with document for document and an array with window for window.`, function() { 44 | expect(selfAndParents(window)).toEqual([window]); 45 | expect(selfAndParents(document)).toEqual([document]); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/setAttrSpecs.js: -------------------------------------------------------------------------------- 1 | import _createDomElement from "../dist/internal/dom/_createDomElement.js"; 2 | import setAttr from "../dist/setAttr.js"; 3 | 4 | describe("setAttr", function() { 5 | it("Should add the attribute name to the tag if passed true.", function() { 6 | let singleHTMLElement = _createDomElement(""); 7 | setAttr(singleHTMLElement, "data-active", true); 8 | 9 | expect(singleHTMLElement.hasAttribute("data-active")).toBe(true); 10 | expect(singleHTMLElement.getAttribute("data-active")).toBe(""); 11 | expect(singleHTMLElement.dataset.active).toBe(""); 12 | }); 13 | 14 | it("Should remove the attribute name to the tag if passed false.", function() { 15 | let singleHTMLElement = _createDomElement(""); 16 | setAttr(singleHTMLElement, "data-active", false); 17 | 18 | expect(singleHTMLElement.hasAttribute("data-active")).toBe(false); 19 | expect(singleHTMLElement.getAttribute("data-active")).toBe(null); 20 | expect(singleHTMLElement.dataset.active).toBe(undefined); 21 | }); 22 | 23 | it("Should add the attribute name to the tag if passed a string.", function() { 24 | let oneElement = _createDomElement('Level 42'); 25 | setAttr(oneElement, "data-level", 42); 26 | 27 | expect(oneElement.getAttribute("data-level")).toBe("42"); 28 | expect(oneElement.dataset.level).toBe("42"); 29 | 30 | let otherElement = _createDomElement('News'); 31 | setAttr(otherElement, "class", "button"); 32 | 33 | expect(otherElement.getAttribute("class")).toBe("button"); 34 | expect(otherElement.className).toBe("button"); 35 | }); 36 | 37 | it("Should accept a group of DOM nodes.", function() { 38 | let listElement = _createDomElement('ABC'), 39 | manyElements = listElement.querySelectorAll("li"); 40 | 41 | setAttr(manyElements, "class", "item"); 42 | 43 | expect(manyElements[0].getAttribute("class")).toBe("item"); 44 | expect(manyElements[0].className).toBe("item"); 45 | expect(manyElements[1].getAttribute("class")).toBe("item"); 46 | expect(manyElements[1].className).toBe("item"); 47 | }); 48 | 49 | it("Should accept a group of DOM nodes.", function() { 50 | let domElA = _createDomElement(""), 51 | domElB = _createDomElement(""); 52 | 53 | setAttr([domElA, domElB], "data-level", 42); 54 | 55 | expect(domElA.getAttribute("data-level")).toBe("42"); 56 | expect(domElB.getAttribute("data-level")).toBe("42"); 57 | expect(domElA.dataset.level).toBe("42"); 58 | expect(domElB.dataset.level).toBe("42"); 59 | }); 60 | 61 | it("Should accept a group of DOM nodes.", function() { 62 | let domElA = _createDomElement(""), 63 | domElB = _createDomElement(""); 64 | 65 | domElA.className = "setAttr"; 66 | domElB.className = "setAttr"; 67 | 68 | document.body.appendChild(domElA); 69 | document.body.appendChild(domElB); 70 | 71 | let manyElements = document.querySelectorAll(".setAttr"); 72 | 73 | setAttr(manyElements, "data-level", 42); 74 | 75 | expect(manyElements[0].getAttribute("data-level")).toBe("42"); 76 | expect(manyElements[1].getAttribute("data-level")).toBe("42"); 77 | expect(manyElements[0].dataset.level).toBe("42"); 78 | expect(manyElements[1].dataset.level).toBe("42"); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/setAttrsSpecs.js: -------------------------------------------------------------------------------- 1 | import _createDomElement from "../dist/internal/dom/_createDomElement.js"; 2 | import setAttrs from "../dist/setAttrs.js"; 3 | 4 | describe("setAttrs", function() { 5 | it("Should set many attributes at once.", function() { 6 | let singleHTMLElement = _createDomElement(""); 7 | setAttrs(singleHTMLElement, { 8 | "data-active": true, 9 | "data-level": 42 10 | }); 11 | 12 | expect(singleHTMLElement.hasAttribute("data-active")).toBe(true); 13 | expect(singleHTMLElement.getAttribute("data-active")).toBe(""); 14 | expect(singleHTMLElement.dataset.active).toBe(""); 15 | 16 | expect(singleHTMLElement.hasAttribute("data-level")).toBe(true); 17 | expect(singleHTMLElement.getAttribute("data-level")).toBe("42"); 18 | expect(singleHTMLElement.dataset.level).toBe("42"); 19 | }); 20 | 21 | it("Should set many attributes at once.", function() { 22 | let oneElement = _createDomElement('Level 42'); 23 | 24 | setAttrs(oneElement, { 25 | "data-level": 42, 26 | "class": "level" 27 | }); 28 | 29 | expect(oneElement.getAttribute("data-level")).toBe("42"); 30 | expect(oneElement.getAttribute("class")).toBe("level"); 31 | expect(oneElement.dataset.level).toBe("42"); 32 | expect(oneElement.className).toBe("level"); 33 | }); 34 | 35 | it("Should accept a group of DOM nodes.", function() { 36 | let listElement = _createDomElement('ABC'), 37 | manyElements = listElement.querySelectorAll("li"); 38 | 39 | setAttrs(manyElements, { 40 | "data-level": 42, 41 | "class": "item" 42 | }); 43 | 44 | expect(manyElements[0].getAttribute("data-level")).toBe("42"); 45 | expect(manyElements[0].getAttribute("class")).toBe("item"); 46 | expect(manyElements[0].dataset.level).toBe("42"); 47 | expect(manyElements[0].className).toBe("item"); 48 | }); 49 | 50 | it("Should add the attribute name to the tag if passed true.", function() { 51 | let singleHTMLElement = _createDomElement(""); 52 | setAttrs(singleHTMLElement, { 53 | "data-active": true 54 | }); 55 | 56 | expect(singleHTMLElement.hasAttribute("data-active")).toBe(true); 57 | expect(singleHTMLElement.getAttribute("data-active")).toBe(""); 58 | expect(singleHTMLElement.dataset.active).toBe(""); 59 | }); 60 | 61 | it("Should remove the attribute name to the tag if passed false.", function() { 62 | let singleHTMLElement = _createDomElement(""); 63 | setAttrs(singleHTMLElement, { 64 | "data-active": false 65 | }); 66 | 67 | expect(singleHTMLElement.hasAttribute("data-active")).toBe(false); 68 | expect(singleHTMLElement.getAttribute("data-active")).toBe(null); 69 | expect(singleHTMLElement.dataset.active).toBe(undefined); 70 | }); 71 | 72 | it("Should accept a group of DOM nodes.", function() { 73 | let domElA = _createDomElement(""), 74 | domElB = _createDomElement(""); 75 | 76 | setAttrs([domElA, domElB], { 77 | "data-level": 42 78 | }); 79 | 80 | expect(domElA.getAttribute("data-level")).toBe("42"); 81 | expect(domElB.getAttribute("data-level")).toBe("42"); 82 | expect(domElA.dataset.level).toBe("42"); 83 | expect(domElB.dataset.level).toBe("42"); 84 | }); 85 | 86 | it("Should accept a group of DOM nodes.", function() { 87 | let domElA = _createDomElement(""), 88 | domElB = _createDomElement(""); 89 | 90 | domElA.className = "setAttrs"; 91 | domElB.className = "setAttrs"; 92 | 93 | document.body.appendChild(domElA); 94 | document.body.appendChild(domElB); 95 | 96 | let manyElements = document.querySelectorAll(".setAttrs"); 97 | 98 | setAttrs(manyElements, { 99 | "data-level": 42 100 | }); 101 | 102 | expect(manyElements[0].getAttribute("data-level")).toBe("42"); 103 | expect(manyElements[1].getAttribute("data-level")).toBe("42"); 104 | expect(manyElements[0].dataset.level).toBe("42"); 105 | expect(manyElements[1].dataset.level).toBe("42"); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/timeSinceSpecs.js: -------------------------------------------------------------------------------- 1 | import timeSince from "../dist/timeSince.js"; 2 | 3 | describe("timeSince", function() { 4 | it(`"Should return the time passed since a timestamp, in milliseconds.`, function(done) { 5 | let timestamp = +new Date(), 6 | result = 0; 7 | 8 | setTimeout(() => { 9 | result = timeSince(timestamp); 10 | expect(result >= 230).toBe(true); 11 | done(); 12 | }, 230); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/toAveragePropSpecs.js: -------------------------------------------------------------------------------- 1 | import toAverageProp from "../dist/toAverageProp.js"; 2 | 3 | describe("toAverageProp", function() { 4 | var mockup = [{ 5 | city: "Rio de Janeiro", 6 | temperature: 96, 7 | demographics: { 8 | population: 6.32 9 | } 10 | }, { 11 | city: "São Paulo", 12 | temperature: 82.5, 13 | demographics: { 14 | population: 12.04 15 | } 16 | }, { 17 | city: "Curitiba", 18 | temperature: 70, 19 | demographics: { 20 | population: 1.752 21 | } 22 | }, { 23 | city: "Florianópolis", 24 | temperature: 86, 25 | demographics: { 26 | population: 0.249 27 | } 28 | }]; 29 | 30 | it(`Should conclude that São Paulo has the average temperature amongst the cities in the array.`, function() { 31 | expect( 32 | mockup.reduce(toAverageProp("temperature")) 33 | ).toBe(mockup[1]); // São Paulo 34 | }); 35 | 36 | it(`Should conclude that Rio de Janeiro has the average population amongst the cities in the array.`, function() { 37 | expect( 38 | mockup.reduce(toAverageProp("demographics.population")) 39 | ).toBe(mockup[0]); // Rio de Janeiro 40 | }); 41 | 42 | var test = [{ 43 | n: "a", 44 | p: 2 45 | }, { 46 | n: "b", 47 | p: 4 48 | }, { 49 | n: "c", 50 | p: 6 51 | }, { 52 | n: "d", 53 | p: 2 54 | }, { 55 | n: "e", 56 | p: 4 57 | }]; 58 | 59 | it(`Should return the first result when two or more are found.`, function() { 60 | expect( 61 | test.reduce(toAverageProp("p")) 62 | ).toBe(test[1]); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/toAverageSpecs.js: -------------------------------------------------------------------------------- 1 | import toAverage from "../dist/toAverage.js"; 2 | 3 | describe("toAverage", function() { 4 | it(`Should return the average of all items in an array when used with Array.prototype.reduce().`, function() { 5 | expect( 6 | [3, 5, 7, 9].reduce(toAverage()) 7 | ).toBe(6); 8 | 9 | expect( 10 | [-10, 10].reduce(toAverage()) 11 | ).toBe(0); 12 | }); 13 | 14 | it("Should function without parentheses.", function() { 15 | expect( 16 | [3, 5, 7, 9].reduce(toAverage) 17 | ).toBe(6); 18 | 19 | expect( 20 | [-10, 10].reduce(toAverage) 21 | ).toBe(0); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/toClosestPropertySpecs.js: -------------------------------------------------------------------------------- 1 | import toClosestProp from "../dist/toClosestProp.js"; 2 | 3 | describe("toClosestProp", function() { 4 | var mockup = [{ 5 | city: "Rio de Janeiro", 6 | temperature: 96, 7 | demographics: { 8 | population: 6.32 9 | } 10 | }, { 11 | city: "São Paulo", 12 | temperature: 82.5, 13 | demographics: { 14 | population: 12.04 15 | } 16 | }, { 17 | city: "Curitiba", 18 | temperature: 70, 19 | demographics: { 20 | population: 1.752 21 | } 22 | }, { 23 | city: "Florianópolis", 24 | temperature: 86, 25 | demographics: { 26 | population: 0.249 27 | } 28 | }]; 29 | 30 | it(`Should conclude that Curitiba has the closest temperature to 75 amongst the cities in the array.`, function() { 31 | expect( 32 | mockup.reduce(toClosestProp("temperature", 75)) 33 | ).toBe(mockup[2]); // Curitiba 34 | }); 35 | 36 | it(`Should conclude that Rio de Janeiro has the closest population to 5 amongst the cities in the array.`, function() { 37 | expect( 38 | mockup.reduce(toClosestProp("demographics.population", 5)) 39 | ).toBe(mockup[0]); // Rio de Janeiro 40 | }); 41 | 42 | var test = [{ 43 | n: "a", 44 | p: 2 45 | }, { 46 | n: "b", 47 | p: 4 48 | }, { 49 | n: "c", 50 | p: 6 51 | }, { 52 | n: "d", 53 | p: 2 54 | }, { 55 | n: "e", 56 | p: 4 57 | }]; 58 | 59 | it(`Should return the first result when two or more are found.`, function() { 60 | expect( 61 | test.reduce(toClosestProp("p", 3)) 62 | ).toBe(test[0]); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/toClosestSpecs.js: -------------------------------------------------------------------------------- 1 | import toClosest from "../dist/toClosest.js"; 2 | 3 | describe("toClosest", function() { 4 | it(`Should return the closest value to a specific number in an array when used with Array.prototype.reduce().`, function() { 5 | expect( 6 | [3, 5, 7, 9].reduce(toClosest(6)) 7 | ).toBe(5); 8 | 9 | expect( 10 | [9, 7, 5, 3].reduce(toClosest(6)) 11 | ).toBe(7); 12 | 13 | expect( 14 | [128, 17, -5, 78].reduce(toClosest(6)) 15 | ).toBe(17); 16 | 17 | expect( 18 | [128, 18, -5, 78].reduce(toClosest(6)) 19 | ).toBe(-5); 20 | 21 | expect( 22 | [3, 5, 7, 9].reduce(toClosest(-2)) 23 | ).toBe(3); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/toLargestPropSpecs.js: -------------------------------------------------------------------------------- 1 | import toLargestProp from "../dist/toLargestProp.js"; 2 | 3 | describe("toLargestProp", function() { 4 | var mockup = [{ 5 | city: "Rio de Janeiro", 6 | temperature: 96, 7 | demographics: { 8 | population: 6.32 9 | } 10 | }, { 11 | city: "São Paulo", 12 | temperature: 82.5, 13 | demographics: { 14 | population: 12.04 15 | } 16 | }, { 17 | city: "Curitiba", 18 | temperature: 70, 19 | demographics: { 20 | population: 1.752 21 | } 22 | }, { 23 | city: "Florianópolis", 24 | temperature: 86, 25 | demographics: { 26 | population: 0.249 27 | } 28 | }]; 29 | 30 | it(`Should conclude that Rio de Janeiro has the largest temperature amongst the cities in the array.`, function() { 31 | expect( 32 | mockup.reduce(toLargestProp("temperature")) 33 | ).toBe(mockup[0]); // Rio de Janeiro 34 | }); 35 | 36 | it(`Should conclude that São Paulo has the largest population amongst the cities in the array.`, function() { 37 | expect( 38 | mockup.reduce(toLargestProp("demographics.population")) 39 | ).toBe(mockup[1]); // São Paulo 40 | }); 41 | 42 | var test = [{ 43 | n: "a", 44 | p: 2 45 | }, { 46 | n: "b", 47 | p: 8 48 | }, { 49 | n: "c", 50 | p: 6 51 | }, { 52 | n: "d", 53 | p: 2 54 | }, { 55 | n: "e", 56 | p: 8 57 | }]; 58 | 59 | it(`Should return the first result when two or more are found.`, function() { 60 | expect( 61 | test.reduce(toLargestProp("p")) 62 | ).toBe(test[1]); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/toSmallestPropSpecs.js: -------------------------------------------------------------------------------- 1 | import toSmallestProp from "../dist/toSmallestProp.js"; 2 | 3 | describe("toSmallestProp", function() { 4 | var mockup = [{ 5 | city: "Rio de Janeiro", 6 | temperature: 96, 7 | demographics: { 8 | population: 6.32 9 | } 10 | }, { 11 | city: "São Paulo", 12 | temperature: 82.5, 13 | demographics: { 14 | population: 12.04 15 | } 16 | }, { 17 | city: "Curitiba", 18 | temperature: 70, 19 | demographics: { 20 | population: 1.752 21 | } 22 | }, { 23 | city: "Florianópolis", 24 | temperature: 86, 25 | demographics: { 26 | population: 0.249 27 | } 28 | }]; 29 | 30 | it(`Should conclude that Curitiba has the smallest temperature amongst the cities in the array.`, function() { 31 | expect( 32 | mockup.reduce(toSmallestProp("temperature")) 33 | ).toBe(mockup[2]); // Curitiba 34 | }); 35 | 36 | it(`Should conclude that Florianópolis has the smallest temperature amongst the cities in the array.`, function() { 37 | expect( 38 | mockup.reduce(toSmallestProp("demographics.population")) 39 | ).toBe(mockup[3]); // Florianópolis 40 | }); 41 | 42 | var test = [{ 43 | n: "a", 44 | p: 2 45 | }, { 46 | n: "b", 47 | p: 8 48 | }, { 49 | n: "c", 50 | p: 6 51 | }, { 52 | n: "d", 53 | p: 2 54 | }, { 55 | n: "e", 56 | p: 8 57 | }]; 58 | 59 | it(`Should return the first result when two or more are found.`, function() { 60 | expect( 61 | test.reduce(toSmallestProp("p")) 62 | ).toBe(test[0]); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/toSumSpecs.js: -------------------------------------------------------------------------------- 1 | import toSum from "../dist/toSum.js"; 2 | 3 | describe("toSum", function() { 4 | it(`Should return the sum of all items in an array when used with Array.prototype.reduce().`, function() { 5 | expect( 6 | [3, 5, 7, 9].reduce(toSum()) 7 | ).toBe(24); 8 | 9 | expect( 10 | [-10, 10].reduce(toSum()) 11 | ).toBe(0); 12 | }); 13 | 14 | it("Should work without parentheses", function() { 15 | expect( 16 | [3, 5, 7, 9].reduce(toSum) 17 | ).toBe(24); 18 | 19 | expect( 20 | [-10, 10].reduce(toSum) 21 | ).toBe(0); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/triggerSpecs.js: -------------------------------------------------------------------------------- 1 | import trigger from "../dist/trigger.js"; 2 | 3 | describe("trigger", function() { 4 | it(`Should trigger a custom event, captured by the emmiter itself.`, function(done) { 5 | let eventCallback = jasmine.createSpy("eventCallback"), 6 | popupLayer = document.createElement("div"); 7 | 8 | document.body.appendChild(popupLayer); 9 | 10 | popupLayer.addEventListener("open", evt => { 11 | eventCallback(); 12 | expect(eventCallback).toHaveBeenCalled(); 13 | done(); 14 | }); 15 | 16 | trigger(popupLayer, "open", false); 17 | }); 18 | 19 | it(`Should trigger a custom event, captured by window because the event bubbles.`, function(done) { 20 | let eventCallback = jasmine.createSpy("eventCallback"), 21 | popupLayer = document.createElement("div"); 22 | 23 | document.body.appendChild(popupLayer); 24 | 25 | window.addEventListener("open", evt => { 26 | eventCallback(); 27 | expect(eventCallback).toHaveBeenCalled(); 28 | done(); 29 | }); 30 | 31 | trigger(popupLayer, "open", true); // bubbles 32 | }); 33 | 34 | it(`Should trigger a custom event, not captured by window because the event does not bubble.`, function(done) { 35 | let eventCallback = jasmine.createSpy("eventCallback"), 36 | popupLayer = document.createElement("div"); 37 | 38 | document.body.appendChild(popupLayer); 39 | 40 | window.addEventListener("open", evt => { 41 | eventCallback(); 42 | }); 43 | 44 | trigger(popupLayer, "open", false); // does not bubble 45 | 46 | setTimeout(() => { 47 | expect(eventCallback).not.toHaveBeenCalled(); 48 | done(); 49 | }, 100); 50 | }); 51 | 52 | it(`Should be able to access event details.`, function(done) { 53 | let popupLayer = document.createElement("div"); 54 | 55 | document.body.appendChild(popupLayer); 56 | 57 | popupLayer.addEventListener("open", evt => { 58 | expect(evt.detail).toBe("customDetail"); 59 | done(); 60 | }); 61 | 62 | trigger(popupLayer, "open", false, false, "customDetail"); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/waitInPromiseSpecs.js: -------------------------------------------------------------------------------- 1 | import waitInPromise from "../dist/waitInPromise.js"; 2 | 3 | describe("waitInPromise", function() { 4 | it(`Should delay the chaining of the Promise by 100ms and pass along the previously resolved value to the next .then().`, function(done) { 5 | let timerCallback = jasmine.createSpy("timerCallback"); 6 | 7 | Promise.resolve("previousValue") 8 | .then(waitInPromise(100)) 9 | .then(value => expect(value).toBe("previousValue")) 10 | .then(timerCallback) 11 | .then(() => expect(timerCallback).toHaveBeenCalled()) 12 | .then(done); 13 | 14 | expect(timerCallback).not.toHaveBeenCalled(); 15 | }); 16 | 17 | it(`Should resolve the Promise without delay if the parameter passed is not a number.`, function(done) { 18 | let timerCallback = jasmine.createSpy("timerCallback"); 19 | 20 | Promise.resolve("previousValue") 21 | .then(waitInPromise(undefined)) 22 | .then(timerCallback); 23 | 24 | setTimeout(function() { 25 | expect(timerCallback).toHaveBeenCalled(); 26 | done(); 27 | }, 10); 28 | }); 29 | 30 | it("Should pass along the original Promise argument", function(done) { 31 | let timerCallback = jasmine.createSpy("timerCallback"); 32 | 33 | Promise.resolve("previousValue") 34 | .then(waitInPromise(undefined)) 35 | .then(value => expect(value).toBe("previousValue")) 36 | .then(timerCallback); 37 | 38 | setTimeout(function() { 39 | expect(timerCallback).toHaveBeenCalled(); 40 | done(); 41 | }, 10); 42 | }); 43 | }); 44 | --------------------------------------------------------------------------------
Level 42