├── .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 | ![](https://canivete.leofavre.com/media/canivete.svg) 2 | 3 | # Canivete ![](https://travis-ci.org/leofavre/canivete.svg?branch=master) 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 `