From 9f0ef3645655ac8325c449eacdc1684fa2dddf69 Mon Sep 17 00:00:00 2001 From: GuyWhoCodes Date: Mon, 22 Mar 2021 21:33:12 -0700 Subject: [PATCH 01/38] fixed help command not recognizing perms? --- Commands/help.js | 6 +- Commands/search.js | 13 +- node_modules/fuse.js/CHANGELOG.md | 269 + node_modules/fuse.js/LICENSE | 201 + node_modules/fuse.js/README.md | 40 + .../fuse.js/dist/fuse.basic.common.js | 1461 ++++ node_modules/fuse.js/dist/fuse.basic.esm.js | 1246 ++++ .../fuse.js/dist/fuse.basic.esm.min.js | 9 + node_modules/fuse.js/dist/fuse.basic.js | 1467 ++++ node_modules/fuse.js/dist/fuse.basic.min.js | 9 + node_modules/fuse.js/dist/fuse.common.js | 2249 ++++++ node_modules/fuse.js/dist/fuse.d.ts | 293 + node_modules/fuse.js/dist/fuse.esm.js | 1782 +++++ node_modules/fuse.js/dist/fuse.esm.min.js | 9 + node_modules/fuse.js/dist/fuse.js | 2255 ++++++ node_modules/fuse.js/dist/fuse.min.js | 9 + node_modules/fuse.js/package.json | 132 + node_modules/jaro-winkler/.jshintrc | 23 + node_modules/jaro-winkler/.npmignore | 3 + node_modules/jaro-winkler/LICENSE | 22 + node_modules/jaro-winkler/README.md | 79 + node_modules/jaro-winkler/index.js | 100 + node_modules/jaro-winkler/package.json | 61 + node_modules/jaro-winkler/test/index.html | 19 + node_modules/jaro-winkler/test/test.js | 29 + node_modules/jaro-winkler/test/vendor/chai.js | 5332 ++++++++++++++ .../jaro-winkler/test/vendor/mocha.css | 270 + .../jaro-winkler/test/vendor/mocha.js | 6467 +++++++++++++++++ package-lock.json | 10 + package.json | 2 + 30 files changed, 23864 insertions(+), 3 deletions(-) create mode 100644 node_modules/fuse.js/CHANGELOG.md create mode 100644 node_modules/fuse.js/LICENSE create mode 100644 node_modules/fuse.js/README.md create mode 100644 node_modules/fuse.js/dist/fuse.basic.common.js create mode 100644 node_modules/fuse.js/dist/fuse.basic.esm.js create mode 100644 node_modules/fuse.js/dist/fuse.basic.esm.min.js create mode 100644 node_modules/fuse.js/dist/fuse.basic.js create mode 100644 node_modules/fuse.js/dist/fuse.basic.min.js create mode 100644 node_modules/fuse.js/dist/fuse.common.js create mode 100644 node_modules/fuse.js/dist/fuse.d.ts create mode 100644 node_modules/fuse.js/dist/fuse.esm.js create mode 100644 node_modules/fuse.js/dist/fuse.esm.min.js create mode 100644 node_modules/fuse.js/dist/fuse.js create mode 100644 node_modules/fuse.js/dist/fuse.min.js create mode 100644 node_modules/fuse.js/package.json create mode 100644 node_modules/jaro-winkler/.jshintrc create mode 100644 node_modules/jaro-winkler/.npmignore create mode 100644 node_modules/jaro-winkler/LICENSE create mode 100644 node_modules/jaro-winkler/README.md create mode 100644 node_modules/jaro-winkler/index.js create mode 100644 node_modules/jaro-winkler/package.json create mode 100644 node_modules/jaro-winkler/test/index.html create mode 100644 node_modules/jaro-winkler/test/test.js create mode 100644 node_modules/jaro-winkler/test/vendor/chai.js create mode 100644 node_modules/jaro-winkler/test/vendor/mocha.css create mode 100644 node_modules/jaro-winkler/test/vendor/mocha.js diff --git a/Commands/help.js b/Commands/help.js index 1b67439..e4e3501 100644 --- a/Commands/help.js +++ b/Commands/help.js @@ -54,9 +54,11 @@ module.exports = { } if (message.guild.id != "587765474297905158") { helpEmbed.fields = helpEmbed.fields.slice(0,3) - } else if (message.member.roles.cache.find(role => role.name != "Discord Staff" || role.name != "Contributor" || role.name != "Discord Management")) { + } else if (message.member.roles.cache.find(role => role.name == "Discord Staff" || role.name == "Contributor" || role.name == "Discord Management")) { + //does nothing + } else { helpEmbed.fields = helpEmbed.fields.slice(0,5) - } + } helpEmbed.fields.push({name: "_ _", value: "**Powered by the [Skyblock Community](https://discord.com/invite/hysky)**"}) helpEmbed.timestamp = new Date() diff --git a/Commands/search.js b/Commands/search.js index ca5d157..0504466 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -20,4 +20,15 @@ module.exports = { message.channel.send("Oops! Something went wrong. Error Message: " + err) }) } -} \ No newline at end of file +} + +// https://fusejs.io/ +// https://www.npmjs.com/package/jaro-winkler +// https://docs.mongodb.com/drivers/node/usage-examples/findOne/ +// http://mongodb.github.io/node-mongodb-native/3.6/api/ +// Command to run test file for this is: node ./Commands/search.js +// const fuse = require('fuse.js') +// const distance = require('jaro-winkler') + +// the distance from jaro-winkler should be first algo to use. fuse is backup algo. +// might need to pull from entire db? need to read into that \ No newline at end of file diff --git a/node_modules/fuse.js/CHANGELOG.md b/node_modules/fuse.js/CHANGELOG.md new file mode 100644 index 0000000..7971628 --- /dev/null +++ b/node_modules/fuse.js/CHANGELOG.md @@ -0,0 +1,269 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +### [6.4.6](https://github.com/krisk/Fuse/compare/v6.4.5...v6.4.6) (2021-01-05) + + +### Bug Fixes + +* **typescript:** fix search typings ([94766b2](https://github.com/krisk/Fuse/commit/94766b2ffcc2be0e5f15daa9a29cd92adbe2647a)), closes [#527](https://github.com/krisk/Fuse/issues/527) + +### [6.4.5](https://github.com/krisk/Fuse/compare/v6.4.4...v6.4.5) (2021-01-01) + + +### Bug Fixes + +* **ts:** export FuseIndex type ([2e60bee](https://github.com/krisk/Fuse/commit/2e60bee242c7b82f0d014a3a35281b34bc6b62fb)), closes [#519](https://github.com/krisk/Fuse/issues/519) + +### [6.4.4](https://github.com/krisk/Fuse/compare/v6.4.3...v6.4.4) (2020-12-29) + + +### Bug Fixes + +* **extended:** correctly score include-match results ([443c863](https://github.com/krisk/Fuse/commit/443c863c44a48225510057d7597cb743fda2d25f)), closes [#522](https://github.com/krisk/Fuse/issues/522) + +### [6.4.3](https://github.com/krisk/Fuse/compare/v6.4.2...v6.4.3) (2020-10-30) + + +### Bug Fixes + +* **extended:** ignoreLocation when useExtendedSearch is true ([8f67ac9](https://github.com/krisk/Fuse/commit/8f67ac985d3440c20d93ce1e1c5ba66f384ea9bf)), closes [#465](https://github.com/krisk/Fuse/issues/465) + +### [6.4.2](https://github.com/krisk/Fuse/compare/v6.4.1...v6.4.2) (2020-10-20) + + +### Bug Fixes + +* if null in array ([740a500](https://github.com/krisk/Fuse/commit/740a5004763d84b285075a2cece4f37bc5fa2830)) + +### [6.4.1](https://github.com/krisk/Fuse/compare/v6.4.0...v6.4.1) (2020-07-26) + + +### Bug Fixes + +* handle booleans in the data ([226d868](https://github.com/krisk/Fuse/commit/226d868a1102402e1e773db305ddd3928ae92f79)), closes [#469](https://github.com/krisk/Fuse/issues/469) + +## [6.4.0](https://github.com/krisk/Fuse/compare/v6.3.1...v6.4.0) (2020-06-28) + + +### Features + +* **extended:** add ability to search actual exact string ([350283f](https://github.com/krisk/Fuse/commit/350283f45a9affe05c6b3176bb5a5a037916de58)) + +### [6.3.1](https://github.com/krisk/Fuse/compare/v6.3.0...v6.3.1) (2020-06-24) + + +### Bug Fixes + +* **logical:** scores in logical query operators are ignored ([e357229](https://github.com/krisk/Fuse/commit/e357229846fff585707903c93f556d1562fbabba)), closes [#449](https://github.com/krisk/Fuse/issues/449) + +## [6.3.0](https://github.com/krisk/Fuse/compare/v6.2.1...v6.3.0) (2020-06-23) + + +### Features + +* provide alternative array notation for nested paths ([7077fbe](https://github.com/krisk/Fuse/commit/7077fbe5f40872f9555645dbad2e6729ca55a5d4)), closes [#432](https://github.com/krisk/Fuse/issues/432) + + +### Bug Fixes + +* **typescript:** add types for string and object together for the key property ([85fb211](https://github.com/krisk/Fuse/commit/85fb211a22bf5921ecefab9ecf3f8e2647f46b49)) +* **typescript:** add typing for nested paths with array notation ([dfa4823](https://github.com/krisk/Fuse/commit/dfa48238f9a28600fd36677f958f43bb3cab4c03)) + +# Version 6.0.0 + +- Added [logical query expressions](https://fusejs.io/api/query.html) (#411) +- Added ability to dynamically add/remove items (#412) +- Mix different `options:key` types during intialization (#413) +- Improved indexing performances, as well storage savings (#405, #407) + +# Version 5.2.0 + +- Addresses #390, #376, #382, #385 +- Removed ngram search and extended bitap to search long patterns + +# Version 5.0.9-beta + +- Fixed Fuse global name. Erroenously set as 'Fuse.js' + +# Version 5.0.8-beta + +- Changed bundler to Rollup. +- Added ES6 modules for bundlers and browsers (`fuse.esm.js`) (fixed [#262](#262)) +- Added CommonJS builds (`fuse.common.js`) + +## Breaking Changes + +- The minimified version is finally actually called `fuse.min.js` + +# Version 5.0.7-beta + +- Fixed (#363) + +# Version 5.0.6-beta + +- Fixed (#357) + +# Version 5.0.3-beta + +- A couple of fixes, courtesy of [Daniel Dickinson](https://github.com/cshoredaniel): + - Generate multiple targets with webpack (#359) + - Fixed TypeError (#360) + +# Version 5.0.2-beta + +- Added indexing for increased performance over large lists + - Added `Fuse.createIndex`, which created and returns an index. This function can be used to pre-generate the index, which you can then save, and ultimately pass to the `Fuse` instance. + +## Breaking Changes + +- Removed `id` option +- Changed format of the search results +- Updated TypeScript definitions + +# Version 5.0.1-beta + +- Removed `matchAllTokens` option. + +# Version 5.0.0-beta + +- Added ability to search patterns longer > 32 characters +- Removed `maxPatternLength` option + +# Version 4.1.0-beta + +- Perf optimization on nested array search + +# Version 4.0.4-beta + +- Re-added license information + +# Version 4.0.3-beta + +- Increased Node version + +# Version 4.0.2-beta + +- Added missing tests + +# Version 4.0.1-beta + +- Removed unused codepath +- Fixed case sensititivity check +- Upgraded dev dependencies + +# Version 4.0.0-beta + +- Added extended search [Discussion](https://github.com/krisk/Fuse/issues/356) +- Removed tokenization [Discussion](https://github.com/krisk/Fuse/issues/355) + +# Version 3.6.0 + +- Improved error handling for keys + +# Version 3.5.0 + +- Fixed #341, adjusting weights into the calculation +- Improved performance by ~10% (really can only be seen when you have 10k+ items) + +# Version 3.4.3 + +- Fixed #261 +- Rewrote tests to Jest framework +- Wrote tests for TypeScript typings +- Cleanup build + +# Version 3.4.2 + +- Fixed #288 + +# Version 3.4.1 + +- Ensured `dist/` content is production ready (both full and min versions) #283 + +# Version 3.4.0 + +- Upgraded build tool to Webpack 4. New `dist/` output. + +# Version 3.3.1 + +- Fixed the circular JSON TypeError (#197). Thanks [ThinkTankShark](https://github.com/ThinkTankShark)! + +# Version 3.2.1 + +- Fixed issue in which more fuzzy matches would weaken a score instead of strengthening it (#233) + +# Version 3.2.0 + +- Give better result for exact match when using weighted keys (#192) + +# Version 3.1.0 + +- Added match index location for array key (#183) +- Allow searching deep nested numbers (#189) + +# Version 3.0.5 + +- Escape special characters in search pattern (#168) + +# Version 3.0.4 + +- Random bug fixes (#162) + +# Version 3.0.0 + +- Removed Bower support +- Modified library into a more more palatable architecture, where the Bitap portion is now its own separate module. + +### BREAKING CHANGES + +- Removed `include` option in favor of more explicit booleans: `includeScore` and `includeMatches`. Both are `false` by default. +- Removed `searchFn` option, as this (for now) will remain a Bitap based solution + +# Version 2.7.4 + +- Reverted to previous version, thus fixing breaking changes (a little bit of a version match here) + +# Version 2.6.2 + +- Revert back to previous version + +# Version 2.6.2 + +- Fix typings based on TypeScript guidelines (#129) + +# Version 2.6.0 + +- Added Typescript definition +- Added ability to set min/max matched character lengths when returning the matched indices (#122) + +# Version 2.5.0 + +- Added option to search by matching all tokens (in every record) when `matchAllTokens:true` (#95) + +# Version 2.3.0 + +- Added token separator to options, when `tokenize:true` (#93) +- General code clean up (#88) +- Bunch of other bug fixes + +# Version 2.2.0 + +- Added option to include matched indices (#6) +- Added ability to search with weighted keys (#62) + +# Version 2.1.0-beta + +- Added ability to search with weighted keys (#62) + +# Version 2.0.0 + +- Modified search algorithm to search individual words AND the full string, computing the final score as a function of both. This yields better scoring accuracy (#41) +- Changed exact substrings to not have a score of zero. That is searching for "hell" in "hello" will not yield a score of zero, while searching for "hello" will (#63) +- Added `verbose` option, which will print to the console useful information, mostly for debugging +- Improved code structure. +- Added version information within Fuse itself +- Added this Changelog (#64) +- Added fallback when pattern length is greater than machine word length (i.e, > 32 characters) (#38) +- Allowed results with a value of 0 to be returned (#73) diff --git a/node_modules/fuse.js/LICENSE b/node_modules/fuse.js/LICENSE new file mode 100644 index 0000000..453d70b --- /dev/null +++ b/node_modules/fuse.js/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Kirollos Risk + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/node_modules/fuse.js/README.md b/node_modules/fuse.js/README.md new file mode 100644 index 0000000..7fcd10a --- /dev/null +++ b/node_modules/fuse.js/README.md @@ -0,0 +1,40 @@ +# Fuse.js + +![Node.js CI](https://github.com/krisk/Fuse/workflows/Node.js%20CI/badge.svg) +[![Version](https://img.shields.io/npm/v/fuse.js.svg)](https://www.npmjs.com/package/fuse.js) +[![Downloads](https://img.shields.io/npm/dm/fuse.js.svg)](https://npmcharts.com/compare/fuse.js?minimal=tru) +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +[![Contributors](https://img.shields.io/github/contributors/krisk/fuse.svg)](https://github.com/krisk/Fuse/graphs/contributors) +![License](https://img.shields.io/npm/l/fuse.js.svg) + +## Supporting Fuse.js + +Through contributions, donations, and sponsorship, you allow Fuse.js to thrive. Also, you will be recognized as a beacon of support to open-source developers. + +- [Become a backer or sponsor on **GitHub**.](https://github.com/sponsors/krisk) +- [Become a backer or sponsor on **Patreon**.](https://patreon.com/fusejs) +- [One-time donation via **PayPal**.](https://www.paypal.me/kirorisk) + +--- + +## Introduction + +Fuse.js is a lightweight fuzzy-search, in JavaScript, with zero dependencies. + +### Browser Compatibility + +Fuse.js supports all browsers that are [ES5-compliant](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). + +## Documentation + + +To check out a [live demo](https://fusejs.io/demo.html) and docs, visit [fusejs.io](https://fusejs.io). + +## Develop + +Here's a separate document for [developers](https://github.com/krisk/Fuse/blob/master/DEVELOPERS.md). + +## Contribute + +We've set up a separate document for our +[contribution guidelines](https://github.com/krisk/Fuse/blob/master/CONTRIBUTING.md). diff --git a/node_modules/fuse.js/dist/fuse.basic.common.js b/node_modules/fuse.js/dist/fuse.basic.common.js new file mode 100644 index 0000000..b55f27f --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.basic.common.js @@ -0,0 +1,1461 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +'use strict'; + +function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); +} + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} + +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; +} + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} + +function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; +} + +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); +} // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js + +var INFINITY = 1 / 0; +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + + var result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; +} +function toString(value) { + return value == null ? '' : baseToString(value); +} +function isString(value) { + return typeof value === 'string'; +} +function isNumber(value) { + return typeof value === 'number'; +} // Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js + +function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; +} +function isObject(value) { + return _typeof(value) === 'object'; +} // Checks if `value` is object-like. + +function isObjectLike(value) { + return isObject(value) && value !== null; +} +function isDefined(value) { + return value !== undefined && value !== null; +} +function isBlank(value) { + return !value.trim().length; +} // Gets the `toStringTag` of `value`. +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js + +function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); +} + +var EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; +var LOGICAL_SEARCH_UNAVAILABLE = 'Logical search is not available'; +var INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; +var LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = function LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key) { + return "Invalid value for key ".concat(key); +}; +var PATTERN_LENGTH_TOO_LARGE = function PATTERN_LENGTH_TOO_LARGE(max) { + return "Pattern length exceeds max of ".concat(max, "."); +}; +var MISSING_KEY_PROPERTY = function MISSING_KEY_PROPERTY(name) { + return "Missing ".concat(name, " property in key"); +}; +var INVALID_KEY_WEIGHT_VALUE = function INVALID_KEY_WEIGHT_VALUE(key) { + return "Property 'weight' in key '".concat(key, "' must be a positive integer"); +}; + +var hasOwn = Object.prototype.hasOwnProperty; + +var KeyStore = /*#__PURE__*/function () { + function KeyStore(keys) { + var _this = this; + + _classCallCheck(this, KeyStore); + + this._keys = []; + this._keyMap = {}; + var totalWeight = 0; + keys.forEach(function (key) { + var obj = createKey(key); + totalWeight += obj.weight; + + _this._keys.push(obj); + + _this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); // Normalize weights so that their sum is equal to 1 + + this._keys.forEach(function (key) { + key.weight /= totalWeight; + }); + } + + _createClass(KeyStore, [{ + key: "get", + value: function get(keyId) { + return this._keyMap[keyId]; + } + }, { + key: "keys", + value: function keys() { + return this._keys; + } + }, { + key: "toJSON", + value: function toJSON() { + return JSON.stringify(this._keys); + } + }]); + + return KeyStore; +}(); +function createKey(key) { + var path = null; + var id = null; + var src = null; + var weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + + var name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { + path: path, + id: id, + weight: weight, + src: src + }; +} +function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); +} +function createKeyId(key) { + return isArray(key) ? key.join('.') : key; +} + +function get(obj, path) { + var list = []; + var arr = false; + + var deepGet = function deepGet(obj, path, index) { + if (!isDefined(obj)) { + return; + } + + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + var key = path[index]; + var value = obj[key]; + + if (!isDefined(value)) { + return; + } // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + + + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; // Search each item in the array. + + for (var i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; // Backwards compatibility (since path used to be a string) + + + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; +} + +var MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 +}; +var BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: function sortFn(a, b) { + return a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1; + } +}; +var FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 +}; +var AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false +}; +var Config = _objectSpread2({}, BasicOptions, {}, MatchOptions, {}, FuzzyOptions, {}, AdvancedOptions); + +var SPACE = /[^ ]+/g; // Field-length norm: the shorter the field, the higher the weight. +// Set to 3 decimals to reduce index size. + +function norm() { + var mantissa = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3; + var cache = new Map(); + var m = Math.pow(10, mantissa); + return { + get: function get(value) { + var numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + var norm = 1 / Math.sqrt(numTokens); // In place of `toFixed(mantissa)`, for faster computation + + var n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear: function clear() { + cache.clear(); + } + }; +} + +var FuseIndex = /*#__PURE__*/function () { + function FuseIndex() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$getFn = _ref.getFn, + getFn = _ref$getFn === void 0 ? Config.getFn : _ref$getFn; + + _classCallCheck(this, FuseIndex); + + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + + _createClass(FuseIndex, [{ + key: "setSources", + value: function setSources() { + var docs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.docs = docs; + } + }, { + key: "setIndexRecords", + value: function setIndexRecords() { + var records = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.records = records; + } + }, { + key: "setKeys", + value: function setKeys() { + var _this = this; + + var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.keys = keys; + this._keysMap = {}; + keys.forEach(function (key, idx) { + _this._keysMap[key.id] = idx; + }); + } + }, { + key: "create", + value: function create() { + var _this2 = this; + + if (this.isCreated || !this.docs.length) { + return; + } + + this.isCreated = true; // List is Array + + if (isString(this.docs[0])) { + this.docs.forEach(function (doc, docIndex) { + _this2._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach(function (doc, docIndex) { + _this2._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } // Adds a doc to the end of the index + + }, { + key: "add", + value: function add(doc) { + var idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } // Removes the doc at the specified index of the index + + }, { + key: "removeAt", + value: function removeAt(idx) { + this.records.splice(idx, 1); // Change ref index of every subsquent doc + + for (var i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + }, { + key: "getValueForItemAtKeyId", + value: function getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + }, { + key: "size", + value: function size() { + return this.records.length; + } + }, { + key: "_addString", + value: function _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + + var record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + }, { + key: "_addObject", + value: function _addObject(doc, docIndex) { + var _this3 = this; + + var record = { + i: docIndex, + $: {} + }; // Iterate over every key (i.e, path), and fetch the value at that key + + this.keys.forEach(function (key, keyIndex) { + // console.log(key) + var value = _this3.getFn(doc, key.path); + + if (!isDefined(value)) { + return; + } + + if (isArray(value)) { + (function () { + var subRecords = []; + var stack = [{ + nestedArrIndex: -1, + value: value + }]; + + while (stack.length) { + var _stack$pop = stack.pop(), + nestedArrIndex = _stack$pop.nestedArrIndex, + _value = _stack$pop.value; + + if (!isDefined(_value)) { + continue; + } + + if (isString(_value) && !isBlank(_value)) { + var subRecord = { + v: _value, + i: nestedArrIndex, + n: _this3.norm.get(_value) + }; + subRecords.push(subRecord); + } else if (isArray(_value)) { + _value.forEach(function (item, k) { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + + record.$[keyIndex] = subRecords; + })(); + } else if (!isBlank(value)) { + var subRecord = { + v: value, + n: _this3.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + }, { + key: "toJSON", + value: function toJSON() { + return { + keys: this.keys, + records: this.records + }; + } + }]); + + return FuseIndex; +}(); +function createIndex(keys, docs) { + var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref2$getFn = _ref2.getFn, + getFn = _ref2$getFn === void 0 ? Config.getFn : _ref2$getFn; + + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; +} +function parseIndex(data) { + var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref3$getFn = _ref3.getFn, + getFn = _ref3$getFn === void 0 ? Config.getFn : _ref3$getFn; + + var keys = data.keys, + records = data.records; + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; +} + +function computeScore(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$errors = _ref.errors, + errors = _ref$errors === void 0 ? 0 : _ref$errors, + _ref$currentLocation = _ref.currentLocation, + currentLocation = _ref$currentLocation === void 0 ? 0 : _ref$currentLocation, + _ref$expectedLocation = _ref.expectedLocation, + expectedLocation = _ref$expectedLocation === void 0 ? 0 : _ref$expectedLocation, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + var accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy; + } + + var proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + + return accuracy + proximity / distance; +} + +function convertMaskToIndices() { + var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Config.minMatchCharLength; + var indices = []; + var start = -1; + var end = -1; + var i = 0; + + for (var len = matchmask.length; i < len; i += 1) { + var match = matchmask[i]; + + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + + start = -1; + } + } // (i-1 - start) + 1 => i - start + + + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices; +} + +// Machine word size +var MAX_BITS = 32; + +function search(text, pattern, patternAlphabet) { + var _ref = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + + var patternLen = pattern.length; // Set starting location at beginning text and initialize the alphabet. + + var textLen = text.length; // Handle the case when location > text.length + + var expectedLocation = Math.max(0, Math.min(location, textLen)); // Highest score beyond which we give up. + + var currentThreshold = threshold; // Is there a nearby exact match? (speedup) + + var bestLocation = expectedLocation; // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + + var computeMatches = minMatchCharLength > 1 || includeMatches; // A mask of the matches, used for building the indices + + var matchMask = computeMatches ? Array(textLen) : []; + var index; // Get all exact matches, here for speed up + + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + var score = computeScore(pattern, { + currentLocation: index, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + var i = 0; + + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } // Reset the best location + + + bestLocation = -1; + var lastBitArr = []; + var finalScore = 1; + var binMax = patternLen + textLen; + var mask = 1 << patternLen - 1; + + for (var _i = 0; _i < patternLen; _i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + var binMin = 0; + var binMid = binMax; + + while (binMin < binMid) { + var _score2 = computeScore(pattern, { + errors: _i, + currentLocation: expectedLocation + binMid, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score2 <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } // Use the result from this iteration as the maximum for the next. + + + binMax = binMid; + var start = Math.max(1, expectedLocation - binMid + 1); + var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; // Initialize the bit array + + var bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << _i) - 1; + + for (var j = finish; j >= start; j -= 1) { + var currentLocation = j - 1; + var charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } // First pass: exact match + + + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; // Subsequent passes: fuzzy match + + if (_i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: _i, + currentLocation: currentLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); // This match will almost certainly be better than any existing match. + // But check anyway. + + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; // Already passed `loc`, downhill from here on in. + + if (bestLocation <= expectedLocation) { + break; + } // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + + + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } // No hope for a (better) match at greater error levels. + + + var _score = computeScore(pattern, { + errors: _i + 1, + currentLocation: expectedLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score > currentThreshold) { + break; + } + + lastBitArr = bitArr; + } + + var result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + var indices = convertMaskToIndices(matchMask, minMatchCharLength); + + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result; +} + +function createPatternAlphabet(pattern) { + var mask = {}; + + for (var i = 0, len = pattern.length; i < len; i += 1) { + var char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + + return mask; +} + +var BitapSearch = /*#__PURE__*/function () { + function BitapSearch(pattern) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, BitapSearch); + + this.options = { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + + if (!this.pattern.length) { + return; + } + + var addChunk = function addChunk(pattern, startIndex) { + _this.chunks.push({ + pattern: pattern, + alphabet: createPatternAlphabet(pattern), + startIndex: startIndex + }); + }; + + var len = this.pattern.length; + + if (len > MAX_BITS) { + var i = 0; + var remainder = len % MAX_BITS; + var end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + var startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + _createClass(BitapSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var _this$options = this.options, + isCaseSensitive = _this$options.isCaseSensitive, + includeMatches = _this$options.includeMatches; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } // Exact match + + + if (this.pattern === text) { + var _result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + _result.indices = [[0, text.length - 1]]; + } + + return _result; + } // Otherwise, use Bitap algorithm + + + var _this$options2 = this.options, + location = _this$options2.location, + distance = _this$options2.distance, + threshold = _this$options2.threshold, + findAllMatches = _this$options2.findAllMatches, + minMatchCharLength = _this$options2.minMatchCharLength, + ignoreLocation = _this$options2.ignoreLocation; + var allIndices = []; + var totalScore = 0; + var hasMatches = false; + this.chunks.forEach(function (_ref2) { + var pattern = _ref2.pattern, + alphabet = _ref2.alphabet, + startIndex = _ref2.startIndex; + + var _search = search(text, pattern, alphabet, { + location: location + startIndex, + distance: distance, + threshold: threshold, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + includeMatches: includeMatches, + ignoreLocation: ignoreLocation + }), + isMatch = _search.isMatch, + score = _search.score, + indices = _search.indices; + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } + }); + var result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result; + } + }]); + + return BitapSearch; +}(); + +var registeredSearchers = []; +function createSearcher(pattern, options) { + for (var i = 0, len = registeredSearchers.length; i < len; i += 1) { + var searcherClass = registeredSearchers[i]; + + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + + return new BitapSearch(pattern, options); +} + +var LogicalOperator = { + AND: '$and', + OR: '$or' +}; +var KeyType = { + PATH: '$path', + PATTERN: '$val' +}; + +var isExpression = function isExpression(query) { + return !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); +}; + +var isPath = function isPath(query) { + return !!query[KeyType.PATH]; +}; + +var isLeaf = function isLeaf(query) { + return !isArray(query) && isObject(query) && !isExpression(query); +}; + +var convertToExplicit = function convertToExplicit(query) { + return _defineProperty({}, LogicalOperator.AND, Object.keys(query).map(function (key) { + return _defineProperty({}, key, query[key]); + })); +}; // When `auto` is `true`, the parse function will infer and initialize and add +// the appropriate `Searcher` instance + + +function parse(query, options) { + var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref3$auto = _ref3.auto, + auto = _ref3$auto === void 0 ? true : _ref3$auto; + + var next = function next(query) { + var keys = Object.keys(query); + var isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + + if (isLeaf(query)) { + var key = isQueryPath ? query[KeyType.PATH] : keys[0]; + var pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + + var obj = { + keyId: createKeyId(key), + pattern: pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj; + } + + var node = { + children: [], + operator: keys[0] + }; + keys.forEach(function (key) { + var value = query[key]; + + if (isArray(value)) { + value.forEach(function (item) { + node.children.push(next(item)); + }); + } + }); + return node; + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query); +} + +function computeScore$1(results, _ref) { + var _ref$ignoreFieldNorm = _ref.ignoreFieldNorm, + ignoreFieldNorm = _ref$ignoreFieldNorm === void 0 ? Config.ignoreFieldNorm : _ref$ignoreFieldNorm; + results.forEach(function (result) { + var totalScore = 1; + result.matches.forEach(function (_ref2) { + var key = _ref2.key, + norm = _ref2.norm, + score = _ref2.score; + var weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); +} + +function transformMatches(result, data) { + var matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return; + } + + matches.forEach(function (match) { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + + var indices = match.indices, + value = match.value; + var obj = { + indices: indices, + value: value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); +} + +function transformScore(result, data) { + data.score = result.score; +} + +function format(results, docs) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$includeScore = _ref.includeScore, + includeScore = _ref$includeScore === void 0 ? Config.includeScore : _ref$includeScore; + + var transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(function (result) { + var idx = result.idx; + var data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach(function (transformer) { + transformer(result, data); + }); + } + + return data; + }); +} + +var Fuse = /*#__PURE__*/function () { + function Fuse(docs) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var index = arguments.length > 2 ? arguments[2] : undefined; + + _classCallCheck(this, Fuse); + + this.options = _objectSpread2({}, Config, {}, options); + + if (this.options.useExtendedSearch && !false) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + + _createClass(Fuse, [{ + key: "setCollection", + value: function setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + }, { + key: "add", + value: function add(doc) { + if (!isDefined(doc)) { + return; + } + + this._docs.push(doc); + + this._myIndex.add(doc); + } + }, { + key: "remove", + value: function remove() { + var predicate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { + return ( + /* doc, idx */ + false + ); + }; + var results = []; + + for (var i = 0, len = this._docs.length; i < len; i += 1) { + var doc = this._docs[i]; + + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + + return results; + } + }, { + key: "removeAt", + value: function removeAt(idx) { + this._docs.splice(idx, 1); + + this._myIndex.removeAt(idx); + } + }, { + key: "getIndex", + value: function getIndex() { + return this._myIndex; + } + }, { + key: "search", + value: function search(query) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$limit = _ref.limit, + limit = _ref$limit === void 0 ? -1 : _ref$limit; + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + includeScore = _this$options.includeScore, + shouldSort = _this$options.shouldSort, + sortFn = _this$options.sortFn, + ignoreFieldNorm = _this$options.ignoreFieldNorm; + var results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore$1(results, { + ignoreFieldNorm: ignoreFieldNorm + }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches: includeMatches, + includeScore: includeScore + }); + } + }, { + key: "_searchStringList", + value: function _searchStringList(query) { + var searcher = createSearcher(query, this.options); + var records = this._myIndex.records; + var results = []; // Iterate over every string in the index + + records.forEach(function (_ref2) { + var text = _ref2.v, + idx = _ref2.i, + norm = _ref2.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn = searcher.searchIn(text), + isMatch = _searcher$searchIn.isMatch, + score = _searcher$searchIn.score, + indices = _searcher$searchIn.indices; + + if (isMatch) { + results.push({ + item: text, + idx: idx, + matches: [{ + score: score, + value: text, + norm: norm, + indices: indices + }] + }); + } + }); + return results; + } + }, { + key: "_searchLogical", + value: function _searchLogical(query) { + + { + throw new Error(LOGICAL_SEARCH_UNAVAILABLE); + } + } + }, { + key: "_searchObjectList", + value: function _searchObjectList(query) { + var _this2 = this; + + var searcher = createSearcher(query, this.options); + var _this$_myIndex = this._myIndex, + keys = _this$_myIndex.keys, + records = _this$_myIndex.records; + var results = []; // List is Array + + records.forEach(function (_ref5) { + var item = _ref5.$, + idx = _ref5.i; + + if (!isDefined(item)) { + return; + } + + var matches = []; // Iterate over every key (i.e, path), and fetch the value at that key + + keys.forEach(function (key, keyIndex) { + matches.push.apply(matches, _toConsumableArray(_this2._findMatches({ + key: key, + value: item[keyIndex], + searcher: searcher + }))); + }); + + if (matches.length) { + results.push({ + idx: idx, + item: item, + matches: matches + }); + } + }); + return results; + } + }, { + key: "_findMatches", + value: function _findMatches(_ref6) { + var key = _ref6.key, + value = _ref6.value, + searcher = _ref6.searcher; + + if (!isDefined(value)) { + return []; + } + + var matches = []; + + if (isArray(value)) { + value.forEach(function (_ref7) { + var text = _ref7.v, + idx = _ref7.i, + norm = _ref7.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn2 = searcher.searchIn(text), + isMatch = _searcher$searchIn2.isMatch, + score = _searcher$searchIn2.score, + indices = _searcher$searchIn2.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + idx: idx, + norm: norm, + indices: indices + }); + } + }); + } else { + var text = value.v, + norm = value.n; + + var _searcher$searchIn3 = searcher.searchIn(text), + isMatch = _searcher$searchIn3.isMatch, + score = _searcher$searchIn3.score, + indices = _searcher$searchIn3.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + norm: norm, + indices: indices + }); + } + } + + return matches; + } + }]); + + return Fuse; +}(); + +Fuse.version = '6.4.6'; +Fuse.createIndex = createIndex; +Fuse.parseIndex = parseIndex; +Fuse.config = Config; + +{ + Fuse.parseQuery = parse; +} + +module.exports = Fuse; diff --git a/node_modules/fuse.js/dist/fuse.basic.esm.js b/node_modules/fuse.js/dist/fuse.basic.esm.js new file mode 100644 index 0000000..c6482f6 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.basic.esm.js @@ -0,0 +1,1246 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +function isArray(value) { + return !Array.isArray + ? getTag(value) === '[object Array]' + : Array.isArray(value) +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js +const INFINITY = 1 / 0; +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value + } + let result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result +} + +function toString(value) { + return value == null ? '' : baseToString(value) +} + +function isString(value) { + return typeof value === 'string' +} + +function isNumber(value) { + return typeof value === 'number' +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js +function isBoolean(value) { + return ( + value === true || + value === false || + (isObjectLike(value) && getTag(value) == '[object Boolean]') + ) +} + +function isObject(value) { + return typeof value === 'object' +} + +// Checks if `value` is object-like. +function isObjectLike(value) { + return isObject(value) && value !== null +} + +function isDefined(value) { + return value !== undefined && value !== null +} + +function isBlank(value) { + return !value.trim().length +} + +// Gets the `toStringTag` of `value`. +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js +function getTag(value) { + return value == null + ? value === undefined + ? '[object Undefined]' + : '[object Null]' + : Object.prototype.toString.call(value) +} + +const EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; + +const LOGICAL_SEARCH_UNAVAILABLE = 'Logical search is not available'; + +const INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; + +const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = (key) => + `Invalid value for key ${key}`; + +const PATTERN_LENGTH_TOO_LARGE = (max) => + `Pattern length exceeds max of ${max}.`; + +const MISSING_KEY_PROPERTY = (name) => `Missing ${name} property in key`; + +const INVALID_KEY_WEIGHT_VALUE = (key) => + `Property 'weight' in key '${key}' must be a positive integer`; + +const hasOwn = Object.prototype.hasOwnProperty; + +class KeyStore { + constructor(keys) { + this._keys = []; + this._keyMap = {}; + + let totalWeight = 0; + + keys.forEach((key) => { + let obj = createKey(key); + + totalWeight += obj.weight; + + this._keys.push(obj); + this._keyMap[obj.id] = obj; + + totalWeight += obj.weight; + }); + + // Normalize weights so that their sum is equal to 1 + this._keys.forEach((key) => { + key.weight /= totalWeight; + }); + } + get(keyId) { + return this._keyMap[keyId] + } + keys() { + return this._keys + } + toJSON() { + return JSON.stringify(this._keys) + } +} + +function createKey(key) { + let path = null; + let id = null; + let src = null; + let weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')) + } + + const name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)) + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { path, id, weight, src } +} + +function createKeyPath(key) { + return isArray(key) ? key : key.split('.') +} + +function createKeyId(key) { + return isArray(key) ? key.join('.') : key +} + +function get(obj, path) { + let list = []; + let arr = false; + + const deepGet = (obj, path, index) => { + if (!isDefined(obj)) { + return + } + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + let key = path[index]; + + const value = obj[key]; + + if (!isDefined(value)) { + return + } + + // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + if ( + index === path.length - 1 && + (isString(value) || isNumber(value) || isBoolean(value)) + ) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; + // Search each item in the array. + for (let i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; + + // Backwards compatibility (since path used to be a string) + deepGet(obj, isString(path) ? path.split('.') : path, 0); + + return arr ? list : list[0] +} + +const MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 +}; + +const BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: (a, b) => + a.score === b.score ? (a.idx < b.idx ? -1 : 1) : a.score < b.score ? -1 : 1 +}; + +const FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 +}; + +const AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false +}; + +var Config = { + ...BasicOptions, + ...MatchOptions, + ...FuzzyOptions, + ...AdvancedOptions +}; + +const SPACE = /[^ ]+/g; + +// Field-length norm: the shorter the field, the higher the weight. +// Set to 3 decimals to reduce index size. +function norm(mantissa = 3) { + const cache = new Map(); + const m = Math.pow(10, mantissa); + + return { + get(value) { + const numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens) + } + + const norm = 1 / Math.sqrt(numTokens); + + // In place of `toFixed(mantissa)`, for faster computation + const n = parseFloat(Math.round(norm * m) / m); + + cache.set(numTokens, n); + + return n + }, + clear() { + cache.clear(); + } + } +} + +class FuseIndex { + constructor({ getFn = Config.getFn } = {}) { + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + + this.setIndexRecords(); + } + setSources(docs = []) { + this.docs = docs; + } + setIndexRecords(records = []) { + this.records = records; + } + setKeys(keys = []) { + this.keys = keys; + this._keysMap = {}; + keys.forEach((key, idx) => { + this._keysMap[key.id] = idx; + }); + } + create() { + if (this.isCreated || !this.docs.length) { + return + } + + this.isCreated = true; + + // List is Array + if (isString(this.docs[0])) { + this.docs.forEach((doc, docIndex) => { + this._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach((doc, docIndex) => { + this._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } + // Adds a doc to the end of the index + add(doc) { + const idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } + // Removes the doc at the specified index of the index + removeAt(idx) { + this.records.splice(idx, 1); + + // Change ref index of every subsquent doc + for (let i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]] + } + size() { + return this.records.length + } + _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return + } + + let record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + + this.records.push(record); + } + _addObject(doc, docIndex) { + let record = { i: docIndex, $: {} }; + + // Iterate over every key (i.e, path), and fetch the value at that key + this.keys.forEach((key, keyIndex) => { + // console.log(key) + let value = this.getFn(doc, key.path); + + if (!isDefined(value)) { + return + } + + if (isArray(value)) { + let subRecords = []; + const stack = [{ nestedArrIndex: -1, value }]; + + while (stack.length) { + const { nestedArrIndex, value } = stack.pop(); + + if (!isDefined(value)) { + continue + } + + if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + i: nestedArrIndex, + n: this.norm.get(value) + }; + + subRecords.push(subRecord); + } else if (isArray(value)) { + value.forEach((item, k) => { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + record.$[keyIndex] = subRecords; + } else if (!isBlank(value)) { + let subRecord = { + v: value, + n: this.norm.get(value) + }; + + record.$[keyIndex] = subRecord; + } + }); + + this.records.push(record); + } + toJSON() { + return { + keys: this.keys, + records: this.records + } + } +} + +function createIndex(keys, docs, { getFn = Config.getFn } = {}) { + const myIndex = new FuseIndex({ getFn }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex +} + +function parseIndex(data, { getFn = Config.getFn } = {}) { + const { keys, records } = data; + const myIndex = new FuseIndex({ getFn }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex +} + +function computeScore( + pattern, + { + errors = 0, + currentLocation = 0, + expectedLocation = 0, + distance = Config.distance, + ignoreLocation = Config.ignoreLocation + } = {} +) { + const accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy + } + + const proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy + } + + return accuracy + proximity / distance +} + +function convertMaskToIndices( + matchmask = [], + minMatchCharLength = Config.minMatchCharLength +) { + let indices = []; + let start = -1; + let end = -1; + let i = 0; + + for (let len = matchmask.length; i < len; i += 1) { + let match = matchmask[i]; + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + start = -1; + } + } + + // (i-1 - start) + 1 => i - start + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices +} + +// Machine word size +const MAX_BITS = 32; + +function search( + text, + pattern, + patternAlphabet, + { + location = Config.location, + distance = Config.distance, + threshold = Config.threshold, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + includeMatches = Config.includeMatches, + ignoreLocation = Config.ignoreLocation + } = {} +) { + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)) + } + + const patternLen = pattern.length; + // Set starting location at beginning text and initialize the alphabet. + const textLen = text.length; + // Handle the case when location > text.length + const expectedLocation = Math.max(0, Math.min(location, textLen)); + // Highest score beyond which we give up. + let currentThreshold = threshold; + // Is there a nearby exact match? (speedup) + let bestLocation = expectedLocation; + + // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + const computeMatches = minMatchCharLength > 1 || includeMatches; + // A mask of the matches, used for building the indices + const matchMask = computeMatches ? Array(textLen) : []; + + let index; + + // Get all exact matches, here for speed up + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + let score = computeScore(pattern, { + currentLocation: index, + expectedLocation, + distance, + ignoreLocation + }); + + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + let i = 0; + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } + + // Reset the best location + bestLocation = -1; + + let lastBitArr = []; + let finalScore = 1; + let binMax = patternLen + textLen; + + const mask = 1 << (patternLen - 1); + + for (let i = 0; i < patternLen; i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + let binMin = 0; + let binMid = binMax; + + while (binMin < binMid) { + const score = computeScore(pattern, { + errors: i, + currentLocation: expectedLocation + binMid, + expectedLocation, + distance, + ignoreLocation + }); + + if (score <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } + + // Use the result from this iteration as the maximum for the next. + binMax = binMid; + + let start = Math.max(1, expectedLocation - binMid + 1); + let finish = findAllMatches + ? textLen + : Math.min(expectedLocation + binMid, textLen) + patternLen; + + // Initialize the bit array + let bitArr = Array(finish + 2); + + bitArr[finish + 1] = (1 << i) - 1; + + for (let j = finish; j >= start; j -= 1) { + let currentLocation = j - 1; + let charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } + + // First pass: exact match + bitArr[j] = ((bitArr[j + 1] << 1) | 1) & charMatch; + + // Subsequent passes: fuzzy match + if (i) { + bitArr[j] |= + ((lastBitArr[j + 1] | lastBitArr[j]) << 1) | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: i, + currentLocation, + expectedLocation, + distance, + ignoreLocation + }); + + // This match will almost certainly be better than any existing match. + // But check anyway. + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; + + // Already passed `loc`, downhill from here on in. + if (bestLocation <= expectedLocation) { + break + } + + // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } + + // No hope for a (better) match at greater error levels. + const score = computeScore(pattern, { + errors: i + 1, + currentLocation: expectedLocation, + expectedLocation, + distance, + ignoreLocation + }); + + if (score > currentThreshold) { + break + } + + lastBitArr = bitArr; + } + + const result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + const indices = convertMaskToIndices(matchMask, minMatchCharLength); + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result +} + +function createPatternAlphabet(pattern) { + let mask = {}; + + for (let i = 0, len = pattern.length; i < len; i += 1) { + const char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | (1 << (len - i - 1)); + } + + return mask +} + +class BitapSearch { + constructor( + pattern, + { + location = Config.location, + threshold = Config.threshold, + distance = Config.distance, + includeMatches = Config.includeMatches, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + isCaseSensitive = Config.isCaseSensitive, + ignoreLocation = Config.ignoreLocation + } = {} + ) { + this.options = { + location, + threshold, + distance, + includeMatches, + findAllMatches, + minMatchCharLength, + isCaseSensitive, + ignoreLocation + }; + + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + + this.chunks = []; + + if (!this.pattern.length) { + return + } + + const addChunk = (pattern, startIndex) => { + this.chunks.push({ + pattern, + alphabet: createPatternAlphabet(pattern), + startIndex + }); + }; + + const len = this.pattern.length; + + if (len > MAX_BITS) { + let i = 0; + const remainder = len % MAX_BITS; + const end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + const startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + searchIn(text) { + const { isCaseSensitive, includeMatches } = this.options; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } + + // Exact match + if (this.pattern === text) { + let result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + result.indices = [[0, text.length - 1]]; + } + + return result + } + + // Otherwise, use Bitap algorithm + const { + location, + distance, + threshold, + findAllMatches, + minMatchCharLength, + ignoreLocation + } = this.options; + + let allIndices = []; + let totalScore = 0; + let hasMatches = false; + + this.chunks.forEach(({ pattern, alphabet, startIndex }) => { + const { isMatch, score, indices } = search(text, pattern, alphabet, { + location: location + startIndex, + distance, + threshold, + findAllMatches, + minMatchCharLength, + includeMatches, + ignoreLocation + }); + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [...allIndices, ...indices]; + } + }); + + let result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result + } +} + +const registeredSearchers = []; + +function createSearcher(pattern, options) { + for (let i = 0, len = registeredSearchers.length; i < len; i += 1) { + let searcherClass = registeredSearchers[i]; + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options) + } + } + + return new BitapSearch(pattern, options) +} + +const LogicalOperator = { + AND: '$and', + OR: '$or' +}; + +const KeyType = { + PATH: '$path', + PATTERN: '$val' +}; + +const isExpression = (query) => + !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); + +const isPath = (query) => !!query[KeyType.PATH]; + +const isLeaf = (query) => + !isArray(query) && isObject(query) && !isExpression(query); + +const convertToExplicit = (query) => ({ + [LogicalOperator.AND]: Object.keys(query).map((key) => ({ + [key]: query[key] + })) +}); + +// When `auto` is `true`, the parse function will infer and initialize and add +// the appropriate `Searcher` instance +function parse(query, options, { auto = true } = {}) { + const next = (query) => { + let keys = Object.keys(query); + + const isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)) + } + + if (isLeaf(query)) { + const key = isQueryPath ? query[KeyType.PATH] : keys[0]; + + const pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)) + } + + const obj = { + keyId: createKeyId(key), + pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj + } + + let node = { + children: [], + operator: keys[0] + }; + + keys.forEach((key) => { + const value = query[key]; + + if (isArray(value)) { + value.forEach((item) => { + node.children.push(next(item)); + }); + } + }); + + return node + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query) +} + +// Practical scoring function +function computeScore$1( + results, + { ignoreFieldNorm = Config.ignoreFieldNorm } +) { + results.forEach((result) => { + let totalScore = 1; + + result.matches.forEach(({ key, norm, score }) => { + const weight = key ? key.weight : null; + + totalScore *= Math.pow( + score === 0 && weight ? Number.EPSILON : score, + (weight || 1) * (ignoreFieldNorm ? 1 : norm) + ); + }); + + result.score = totalScore; + }); +} + +function transformMatches(result, data) { + const matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return + } + + matches.forEach((match) => { + if (!isDefined(match.indices) || !match.indices.length) { + return + } + + const { indices, value } = match; + + let obj = { + indices, + value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); +} + +function transformScore(result, data) { + data.score = result.score; +} + +function format( + results, + docs, + { + includeMatches = Config.includeMatches, + includeScore = Config.includeScore + } = {} +) { + const transformers = []; + + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + + return results.map((result) => { + const { idx } = result; + + const data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach((transformer) => { + transformer(result, data); + }); + } + + return data + }) +} + +class Fuse { + constructor(docs, options = {}, index) { + this.options = { ...Config, ...options }; + + if ( + this.options.useExtendedSearch && + !false + ) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE) + } + + this._keyStore = new KeyStore(this.options.keys); + + this.setCollection(docs, index); + } + + setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE) + } + + this._myIndex = + index || + createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + + add(doc) { + if (!isDefined(doc)) { + return + } + + this._docs.push(doc); + this._myIndex.add(doc); + } + + remove(predicate = (/* doc, idx */) => false) { + const results = []; + + for (let i = 0, len = this._docs.length; i < len; i += 1) { + const doc = this._docs[i]; + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + + results.push(doc); + } + } + + return results + } + + removeAt(idx) { + this._docs.splice(idx, 1); + this._myIndex.removeAt(idx); + } + + getIndex() { + return this._myIndex + } + + search(query, { limit = -1 } = {}) { + const { + includeMatches, + includeScore, + shouldSort, + sortFn, + ignoreFieldNorm + } = this.options; + + let results = isString(query) + ? isString(this._docs[0]) + ? this._searchStringList(query) + : this._searchObjectList(query) + : this._searchLogical(query); + + computeScore$1(results, { ignoreFieldNorm }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches, + includeScore + }) + } + + _searchStringList(query) { + const searcher = createSearcher(query, this.options); + const { records } = this._myIndex; + const results = []; + + // Iterate over every string in the index + records.forEach(({ v: text, i: idx, n: norm }) => { + if (!isDefined(text)) { + return + } + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + results.push({ + item: text, + idx, + matches: [{ score, value: text, norm, indices }] + }); + } + }); + + return results + } + + _searchLogical(query) { + { + throw new Error(LOGICAL_SEARCH_UNAVAILABLE) + } + } + + _searchObjectList(query) { + const searcher = createSearcher(query, this.options); + const { keys, records } = this._myIndex; + const results = []; + + // List is Array + records.forEach(({ $: item, i: idx }) => { + if (!isDefined(item)) { + return + } + + let matches = []; + + // Iterate over every key (i.e, path), and fetch the value at that key + keys.forEach((key, keyIndex) => { + matches.push( + ...this._findMatches({ + key, + value: item[keyIndex], + searcher + }) + ); + }); + + if (matches.length) { + results.push({ + idx, + item, + matches + }); + } + }); + + return results + } + _findMatches({ key, value, searcher }) { + if (!isDefined(value)) { + return [] + } + + let matches = []; + + if (isArray(value)) { + value.forEach(({ v: text, i: idx, n: norm }) => { + if (!isDefined(text)) { + return + } + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + matches.push({ + score, + key, + value: text, + idx, + norm, + indices + }); + } + }); + } else { + const { v: text, n: norm } = value; + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + matches.push({ score, key, value: text, norm, indices }); + } + } + + return matches + } +} + +Fuse.version = '6.4.6'; +Fuse.createIndex = createIndex; +Fuse.parseIndex = parseIndex; +Fuse.config = Config; + +{ + Fuse.parseQuery = parse; +} + +export default Fuse; diff --git a/node_modules/fuse.js/dist/fuse.basic.esm.min.js b/node_modules/fuse.js/dist/fuse.basic.esm.min.js new file mode 100644 index 0000000..4c2646a --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.basic.esm.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +function t(t){return Array.isArray?Array.isArray(t):"[object Array]"===o(t)}function e(t){return"string"==typeof t}function n(t){return"number"==typeof t}function s(t){return!0===t||!1===t||function(t){return function(t){return"object"==typeof t}(t)&&null!==t}(t)&&"[object Boolean]"==o(t)}function i(t){return null!=t}function r(t){return!t.trim().length}function o(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":Object.prototype.toString.call(t)}const c=Object.prototype.hasOwnProperty;class h{constructor(t){this._keys=[],this._keyMap={};let e=0;t.forEach(t=>{let n=a(t);e+=n.weight,this._keys.push(n),this._keyMap[n.id]=n,e+=n.weight}),this._keys.forEach(t=>{t.weight/=e})}get(t){return this._keyMap[t]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function a(n){let s=null,i=null,r=null,o=1;if(e(n)||t(n))r=n,s=l(n),i=d(n);else{if(!c.call(n,"name"))throw new Error((t=>`Missing ${t} property in key`)("name"));const t=n.name;if(r=t,c.call(n,"weight")&&(o=n.weight,o<=0))throw new Error((t=>`Property 'weight' in key '${t}' must be a positive integer`)(t));s=l(t),i=d(t)}return{path:s,id:i,weight:o,src:r}}function l(e){return t(e)?e:e.split(".")}function d(e){return t(e)?e.join("."):e}var u={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(t,e)=>t.score===e.score?t.idx{if(i(r))if(o[l]){const d=r[o[l]];if(!i(d))return;if(l===o.length-1&&(e(d)||n(d)||s(d)))c.push(function(t){return null==t?"":function(t){if("string"==typeof t)return t;let e=t+"";return"0"==e&&1/t==-1/0?"-0":e}(t)}(d));else if(t(d)){h=!0;for(let t=0,e=d.length;t{this._keysMap[t.id]=e})}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,e(this.docs[0])?this.docs.forEach((t,e)=>{this._addString(t,e)}):this.docs.forEach((t,e)=>{this._addObject(t,e)}),this.norm.clear())}add(t){const n=this.size();e(t)?this._addString(t,n):this._addObject(t,n)}removeAt(t){this.records.splice(t,1);for(let e=t,n=this.size();e{let h=this.getFn(n,s.path);if(i(h))if(t(h)){let n=[];const s=[{nestedArrIndex:-1,value:h}];for(;s.length;){const{nestedArrIndex:o,value:c}=s.pop();if(i(c))if(e(c)&&!r(c)){let t={v:c,i:o,n:this.norm.get(c)};n.push(t)}else t(c)&&c.forEach((t,e)=>{s.push({nestedArrIndex:e,value:t})})}o.$[c]=n}else if(!r(h)){let t={v:h,n:this.norm.get(h)};o.$[c]=t}}),this.records.push(o)}toJSON(){return{keys:this.keys,records:this.records}}}function p(t,e,{getFn:n=u.getFn}={}){const s=new g({getFn:n});return s.setKeys(t.map(a)),s.setSources(e),s.create(),s}function m(t,{errors:e=0,currentLocation:n=0,expectedLocation:s=0,distance:i=u.distance,ignoreLocation:r=u.ignoreLocation}={}){const o=e/t.length;if(r)return o;const c=Math.abs(s-n);return i?o+c/i:c?1:o}function y(t,e,n,{location:s=u.location,distance:i=u.distance,threshold:r=u.threshold,findAllMatches:o=u.findAllMatches,minMatchCharLength:c=u.minMatchCharLength,includeMatches:h=u.includeMatches,ignoreLocation:a=u.ignoreLocation}={}){if(e.length>32)throw new Error(`Pattern length exceeds max of ${32}.`);const l=e.length,d=t.length,f=Math.max(0,Math.min(s,d));let g=r,p=f;const y=c>1||h,M=y?Array(d):[];let x;for(;(x=t.indexOf(e,p))>-1;){let t=m(e,{currentLocation:x,expectedLocation:f,distance:i,ignoreLocation:a});if(g=Math.min(t,g),p=x+l,y){let t=0;for(;t=h;r-=1){let o=r-1,c=n[t.charAt(o)];if(y&&(M[o]=+!!c),x[r]=(x[r+1]<<1|1)&c,s&&(x[r]|=(L[r+1]|L[r])<<1|1|L[r+1]),x[r]&v&&(_=m(e,{errors:s,currentLocation:o,expectedLocation:f,distance:i,ignoreLocation:a}),_<=g)){if(g=_,p=o,p<=f)break;h=Math.max(1,2*f-p)}}if(m(e,{errors:s+1,currentLocation:f,expectedLocation:f,distance:i,ignoreLocation:a})>g)break;L=x}const w={isMatch:p>=0,score:Math.max(.001,_)};if(y){const t=function(t=[],e=u.minMatchCharLength){let n=[],s=-1,i=-1,r=0;for(let o=t.length;r=e&&n.push([s,i]),s=-1)}return t[r-1]&&r-s>=e&&n.push([s,r-1]),n}(M,c);t.length?h&&(w.indices=t):w.isMatch=!1}return w}function M(t){let e={};for(let n=0,s=t.length;n{this.chunks.push({pattern:t,alphabet:M(t),startIndex:e})},l=this.pattern.length;if(l>32){let t=0;const e=l%32,n=l-e;for(;t{const{isMatch:g,score:p,indices:m}=y(t,e,u,{location:s+f,distance:i,threshold:r,findAllMatches:o,minMatchCharLength:c,includeMatches:n,ignoreLocation:h});g&&(d=!0),l+=p,g&&m&&(a=[...a,...m])});let u={isMatch:d,score:d?l/this.chunks.length:1};return d&&n&&(u.indices=a),u}}const L=[];function _(t,e){for(let n=0,s=L.length;n{if(!i(t.indices)||!t.indices.length)return;const{indices:n,value:s}=t;let r={indices:n,value:s};t.key&&(r.key=t.key.src),t.idx>-1&&(r.refIndex=t.idx),e.matches.push(r)})}function v(t,e){e.score=t.score}class w{constructor(t,e={},n){if(this.options={...u,...e},this.options.useExtendedSearch)throw new Error("Extended search is not available");this._keyStore=new h(this.options.keys),this.setCollection(t,n)}setCollection(t,e){if(this._docs=t,e&&!(e instanceof g))throw new Error("Incorrect 'index' type");this._myIndex=e||p(this.options.keys,this._docs,{getFn:this.options.getFn})}add(t){i(t)&&(this._docs.push(t),this._myIndex.add(t))}remove(t=(()=>!1)){const e=[];for(let n=0,s=this._docs.length;n{let n=1;t.matches.forEach(({key:t,norm:s,score:i})=>{const r=t?t.weight:null;n*=Math.pow(0===i&&r?Number.EPSILON:i,(r||1)*(e?1:s))}),t.score=n})}(a,{ignoreFieldNorm:h}),o&&a.sort(c),n(s)&&s>-1&&(a=a.slice(0,s)),function(t,e,{includeMatches:n=u.includeMatches,includeScore:s=u.includeScore}={}){const i=[];return n&&i.push(k),s&&i.push(v),t.map(t=>{const{idx:n}=t,s={item:e[n],refIndex:n};return i.length&&i.forEach(e=>{e(t,s)}),s})}(a,this._docs,{includeMatches:i,includeScore:r})}_searchStringList(t){const e=_(t,this.options),{records:n}=this._myIndex,s=[];return n.forEach(({v:t,i:n,n:r})=>{if(!i(t))return;const{isMatch:o,score:c,indices:h}=e.searchIn(t);o&&s.push({item:t,idx:n,matches:[{score:c,value:t,norm:r,indices:h}]})}),s}_searchLogical(t){throw new Error("Logical search is not available")}_searchObjectList(t){const e=_(t,this.options),{keys:n,records:s}=this._myIndex,r=[];return s.forEach(({$:t,i:s})=>{if(!i(t))return;let o=[];n.forEach((n,s)=>{o.push(...this._findMatches({key:n,value:t[s],searcher:e}))}),o.length&&r.push({idx:s,item:t,matches:o})}),r}_findMatches({key:e,value:n,searcher:s}){if(!i(n))return[];let r=[];if(t(n))n.forEach(({v:t,i:n,n:o})=>{if(!i(t))return;const{isMatch:c,score:h,indices:a}=s.searchIn(t);c&&r.push({score:h,key:e,value:t,idx:n,norm:o,indices:a})});else{const{v:t,n:i}=n,{isMatch:o,score:c,indices:h}=s.searchIn(t);o&&r.push({score:c,key:e,value:t,norm:i,indices:h})}return r}}w.version="6.4.6",w.createIndex=p,w.parseIndex=function(t,{getFn:e=u.getFn}={}){const{keys:n,records:s}=t,i=new g({getFn:e});return i.setKeys(n),i.setIndexRecords(s),i},w.config=u;export default w; \ No newline at end of file diff --git a/node_modules/fuse.js/dist/fuse.basic.js b/node_modules/fuse.js/dist/fuse.basic.js new file mode 100644 index 0000000..c9afdc7 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.basic.js @@ -0,0 +1,1467 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Fuse = factory()); +}(this, (function () { 'use strict'; + + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); + } + + function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); + } // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js + + var INFINITY = 1 / 0; + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + + var result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; + } + function toString(value) { + return value == null ? '' : baseToString(value); + } + function isString(value) { + return typeof value === 'string'; + } + function isNumber(value) { + return typeof value === 'number'; + } // Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js + + function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; + } + function isObject(value) { + return _typeof(value) === 'object'; + } // Checks if `value` is object-like. + + function isObjectLike(value) { + return isObject(value) && value !== null; + } + function isDefined(value) { + return value !== undefined && value !== null; + } + function isBlank(value) { + return !value.trim().length; + } // Gets the `toStringTag` of `value`. + // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js + + function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); + } + + var EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; + var LOGICAL_SEARCH_UNAVAILABLE = 'Logical search is not available'; + var INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; + var LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = function LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key) { + return "Invalid value for key ".concat(key); + }; + var PATTERN_LENGTH_TOO_LARGE = function PATTERN_LENGTH_TOO_LARGE(max) { + return "Pattern length exceeds max of ".concat(max, "."); + }; + var MISSING_KEY_PROPERTY = function MISSING_KEY_PROPERTY(name) { + return "Missing ".concat(name, " property in key"); + }; + var INVALID_KEY_WEIGHT_VALUE = function INVALID_KEY_WEIGHT_VALUE(key) { + return "Property 'weight' in key '".concat(key, "' must be a positive integer"); + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var KeyStore = /*#__PURE__*/function () { + function KeyStore(keys) { + var _this = this; + + _classCallCheck(this, KeyStore); + + this._keys = []; + this._keyMap = {}; + var totalWeight = 0; + keys.forEach(function (key) { + var obj = createKey(key); + totalWeight += obj.weight; + + _this._keys.push(obj); + + _this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); // Normalize weights so that their sum is equal to 1 + + this._keys.forEach(function (key) { + key.weight /= totalWeight; + }); + } + + _createClass(KeyStore, [{ + key: "get", + value: function get(keyId) { + return this._keyMap[keyId]; + } + }, { + key: "keys", + value: function keys() { + return this._keys; + } + }, { + key: "toJSON", + value: function toJSON() { + return JSON.stringify(this._keys); + } + }]); + + return KeyStore; + }(); + function createKey(key) { + var path = null; + var id = null; + var src = null; + var weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + + var name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { + path: path, + id: id, + weight: weight, + src: src + }; + } + function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); + } + function createKeyId(key) { + return isArray(key) ? key.join('.') : key; + } + + function get(obj, path) { + var list = []; + var arr = false; + + var deepGet = function deepGet(obj, path, index) { + if (!isDefined(obj)) { + return; + } + + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + var key = path[index]; + var value = obj[key]; + + if (!isDefined(value)) { + return; + } // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + + + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; // Search each item in the array. + + for (var i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; // Backwards compatibility (since path used to be a string) + + + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; + } + + var MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 + }; + var BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: function sortFn(a, b) { + return a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1; + } + }; + var FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 + }; + var AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false + }; + var Config = _objectSpread2({}, BasicOptions, {}, MatchOptions, {}, FuzzyOptions, {}, AdvancedOptions); + + var SPACE = /[^ ]+/g; // Field-length norm: the shorter the field, the higher the weight. + // Set to 3 decimals to reduce index size. + + function norm() { + var mantissa = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3; + var cache = new Map(); + var m = Math.pow(10, mantissa); + return { + get: function get(value) { + var numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + var norm = 1 / Math.sqrt(numTokens); // In place of `toFixed(mantissa)`, for faster computation + + var n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear: function clear() { + cache.clear(); + } + }; + } + + var FuseIndex = /*#__PURE__*/function () { + function FuseIndex() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$getFn = _ref.getFn, + getFn = _ref$getFn === void 0 ? Config.getFn : _ref$getFn; + + _classCallCheck(this, FuseIndex); + + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + + _createClass(FuseIndex, [{ + key: "setSources", + value: function setSources() { + var docs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.docs = docs; + } + }, { + key: "setIndexRecords", + value: function setIndexRecords() { + var records = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.records = records; + } + }, { + key: "setKeys", + value: function setKeys() { + var _this = this; + + var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.keys = keys; + this._keysMap = {}; + keys.forEach(function (key, idx) { + _this._keysMap[key.id] = idx; + }); + } + }, { + key: "create", + value: function create() { + var _this2 = this; + + if (this.isCreated || !this.docs.length) { + return; + } + + this.isCreated = true; // List is Array + + if (isString(this.docs[0])) { + this.docs.forEach(function (doc, docIndex) { + _this2._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach(function (doc, docIndex) { + _this2._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } // Adds a doc to the end of the index + + }, { + key: "add", + value: function add(doc) { + var idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } // Removes the doc at the specified index of the index + + }, { + key: "removeAt", + value: function removeAt(idx) { + this.records.splice(idx, 1); // Change ref index of every subsquent doc + + for (var i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + }, { + key: "getValueForItemAtKeyId", + value: function getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + }, { + key: "size", + value: function size() { + return this.records.length; + } + }, { + key: "_addString", + value: function _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + + var record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + }, { + key: "_addObject", + value: function _addObject(doc, docIndex) { + var _this3 = this; + + var record = { + i: docIndex, + $: {} + }; // Iterate over every key (i.e, path), and fetch the value at that key + + this.keys.forEach(function (key, keyIndex) { + // console.log(key) + var value = _this3.getFn(doc, key.path); + + if (!isDefined(value)) { + return; + } + + if (isArray(value)) { + (function () { + var subRecords = []; + var stack = [{ + nestedArrIndex: -1, + value: value + }]; + + while (stack.length) { + var _stack$pop = stack.pop(), + nestedArrIndex = _stack$pop.nestedArrIndex, + _value = _stack$pop.value; + + if (!isDefined(_value)) { + continue; + } + + if (isString(_value) && !isBlank(_value)) { + var subRecord = { + v: _value, + i: nestedArrIndex, + n: _this3.norm.get(_value) + }; + subRecords.push(subRecord); + } else if (isArray(_value)) { + _value.forEach(function (item, k) { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + + record.$[keyIndex] = subRecords; + })(); + } else if (!isBlank(value)) { + var subRecord = { + v: value, + n: _this3.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + }, { + key: "toJSON", + value: function toJSON() { + return { + keys: this.keys, + records: this.records + }; + } + }]); + + return FuseIndex; + }(); + function createIndex(keys, docs) { + var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref2$getFn = _ref2.getFn, + getFn = _ref2$getFn === void 0 ? Config.getFn : _ref2$getFn; + + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; + } + function parseIndex(data) { + var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref3$getFn = _ref3.getFn, + getFn = _ref3$getFn === void 0 ? Config.getFn : _ref3$getFn; + + var keys = data.keys, + records = data.records; + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; + } + + function computeScore(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$errors = _ref.errors, + errors = _ref$errors === void 0 ? 0 : _ref$errors, + _ref$currentLocation = _ref.currentLocation, + currentLocation = _ref$currentLocation === void 0 ? 0 : _ref$currentLocation, + _ref$expectedLocation = _ref.expectedLocation, + expectedLocation = _ref$expectedLocation === void 0 ? 0 : _ref$expectedLocation, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + var accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy; + } + + var proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + + return accuracy + proximity / distance; + } + + function convertMaskToIndices() { + var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Config.minMatchCharLength; + var indices = []; + var start = -1; + var end = -1; + var i = 0; + + for (var len = matchmask.length; i < len; i += 1) { + var match = matchmask[i]; + + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + + start = -1; + } + } // (i-1 - start) + 1 => i - start + + + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices; + } + + // Machine word size + var MAX_BITS = 32; + + function search(text, pattern, patternAlphabet) { + var _ref = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + + var patternLen = pattern.length; // Set starting location at beginning text and initialize the alphabet. + + var textLen = text.length; // Handle the case when location > text.length + + var expectedLocation = Math.max(0, Math.min(location, textLen)); // Highest score beyond which we give up. + + var currentThreshold = threshold; // Is there a nearby exact match? (speedup) + + var bestLocation = expectedLocation; // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + + var computeMatches = minMatchCharLength > 1 || includeMatches; // A mask of the matches, used for building the indices + + var matchMask = computeMatches ? Array(textLen) : []; + var index; // Get all exact matches, here for speed up + + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + var score = computeScore(pattern, { + currentLocation: index, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + var i = 0; + + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } // Reset the best location + + + bestLocation = -1; + var lastBitArr = []; + var finalScore = 1; + var binMax = patternLen + textLen; + var mask = 1 << patternLen - 1; + + for (var _i = 0; _i < patternLen; _i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + var binMin = 0; + var binMid = binMax; + + while (binMin < binMid) { + var _score2 = computeScore(pattern, { + errors: _i, + currentLocation: expectedLocation + binMid, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score2 <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } // Use the result from this iteration as the maximum for the next. + + + binMax = binMid; + var start = Math.max(1, expectedLocation - binMid + 1); + var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; // Initialize the bit array + + var bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << _i) - 1; + + for (var j = finish; j >= start; j -= 1) { + var currentLocation = j - 1; + var charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } // First pass: exact match + + + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; // Subsequent passes: fuzzy match + + if (_i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: _i, + currentLocation: currentLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); // This match will almost certainly be better than any existing match. + // But check anyway. + + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; // Already passed `loc`, downhill from here on in. + + if (bestLocation <= expectedLocation) { + break; + } // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + + + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } // No hope for a (better) match at greater error levels. + + + var _score = computeScore(pattern, { + errors: _i + 1, + currentLocation: expectedLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score > currentThreshold) { + break; + } + + lastBitArr = bitArr; + } + + var result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + var indices = convertMaskToIndices(matchMask, minMatchCharLength); + + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result; + } + + function createPatternAlphabet(pattern) { + var mask = {}; + + for (var i = 0, len = pattern.length; i < len; i += 1) { + var char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + + return mask; + } + + var BitapSearch = /*#__PURE__*/function () { + function BitapSearch(pattern) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, BitapSearch); + + this.options = { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + + if (!this.pattern.length) { + return; + } + + var addChunk = function addChunk(pattern, startIndex) { + _this.chunks.push({ + pattern: pattern, + alphabet: createPatternAlphabet(pattern), + startIndex: startIndex + }); + }; + + var len = this.pattern.length; + + if (len > MAX_BITS) { + var i = 0; + var remainder = len % MAX_BITS; + var end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + var startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + _createClass(BitapSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var _this$options = this.options, + isCaseSensitive = _this$options.isCaseSensitive, + includeMatches = _this$options.includeMatches; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } // Exact match + + + if (this.pattern === text) { + var _result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + _result.indices = [[0, text.length - 1]]; + } + + return _result; + } // Otherwise, use Bitap algorithm + + + var _this$options2 = this.options, + location = _this$options2.location, + distance = _this$options2.distance, + threshold = _this$options2.threshold, + findAllMatches = _this$options2.findAllMatches, + minMatchCharLength = _this$options2.minMatchCharLength, + ignoreLocation = _this$options2.ignoreLocation; + var allIndices = []; + var totalScore = 0; + var hasMatches = false; + this.chunks.forEach(function (_ref2) { + var pattern = _ref2.pattern, + alphabet = _ref2.alphabet, + startIndex = _ref2.startIndex; + + var _search = search(text, pattern, alphabet, { + location: location + startIndex, + distance: distance, + threshold: threshold, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + includeMatches: includeMatches, + ignoreLocation: ignoreLocation + }), + isMatch = _search.isMatch, + score = _search.score, + indices = _search.indices; + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } + }); + var result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result; + } + }]); + + return BitapSearch; + }(); + + var registeredSearchers = []; + function createSearcher(pattern, options) { + for (var i = 0, len = registeredSearchers.length; i < len; i += 1) { + var searcherClass = registeredSearchers[i]; + + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + + return new BitapSearch(pattern, options); + } + + var LogicalOperator = { + AND: '$and', + OR: '$or' + }; + var KeyType = { + PATH: '$path', + PATTERN: '$val' + }; + + var isExpression = function isExpression(query) { + return !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); + }; + + var isPath = function isPath(query) { + return !!query[KeyType.PATH]; + }; + + var isLeaf = function isLeaf(query) { + return !isArray(query) && isObject(query) && !isExpression(query); + }; + + var convertToExplicit = function convertToExplicit(query) { + return _defineProperty({}, LogicalOperator.AND, Object.keys(query).map(function (key) { + return _defineProperty({}, key, query[key]); + })); + }; // When `auto` is `true`, the parse function will infer and initialize and add + // the appropriate `Searcher` instance + + + function parse(query, options) { + var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref3$auto = _ref3.auto, + auto = _ref3$auto === void 0 ? true : _ref3$auto; + + var next = function next(query) { + var keys = Object.keys(query); + var isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + + if (isLeaf(query)) { + var key = isQueryPath ? query[KeyType.PATH] : keys[0]; + var pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + + var obj = { + keyId: createKeyId(key), + pattern: pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj; + } + + var node = { + children: [], + operator: keys[0] + }; + keys.forEach(function (key) { + var value = query[key]; + + if (isArray(value)) { + value.forEach(function (item) { + node.children.push(next(item)); + }); + } + }); + return node; + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query); + } + + function computeScore$1(results, _ref) { + var _ref$ignoreFieldNorm = _ref.ignoreFieldNorm, + ignoreFieldNorm = _ref$ignoreFieldNorm === void 0 ? Config.ignoreFieldNorm : _ref$ignoreFieldNorm; + results.forEach(function (result) { + var totalScore = 1; + result.matches.forEach(function (_ref2) { + var key = _ref2.key, + norm = _ref2.norm, + score = _ref2.score; + var weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); + } + + function transformMatches(result, data) { + var matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return; + } + + matches.forEach(function (match) { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + + var indices = match.indices, + value = match.value; + var obj = { + indices: indices, + value: value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); + } + + function transformScore(result, data) { + data.score = result.score; + } + + function format(results, docs) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$includeScore = _ref.includeScore, + includeScore = _ref$includeScore === void 0 ? Config.includeScore : _ref$includeScore; + + var transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(function (result) { + var idx = result.idx; + var data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach(function (transformer) { + transformer(result, data); + }); + } + + return data; + }); + } + + var Fuse = /*#__PURE__*/function () { + function Fuse(docs) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var index = arguments.length > 2 ? arguments[2] : undefined; + + _classCallCheck(this, Fuse); + + this.options = _objectSpread2({}, Config, {}, options); + + if (this.options.useExtendedSearch && !false) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + + _createClass(Fuse, [{ + key: "setCollection", + value: function setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + }, { + key: "add", + value: function add(doc) { + if (!isDefined(doc)) { + return; + } + + this._docs.push(doc); + + this._myIndex.add(doc); + } + }, { + key: "remove", + value: function remove() { + var predicate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { + return ( + /* doc, idx */ + false + ); + }; + var results = []; + + for (var i = 0, len = this._docs.length; i < len; i += 1) { + var doc = this._docs[i]; + + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + + return results; + } + }, { + key: "removeAt", + value: function removeAt(idx) { + this._docs.splice(idx, 1); + + this._myIndex.removeAt(idx); + } + }, { + key: "getIndex", + value: function getIndex() { + return this._myIndex; + } + }, { + key: "search", + value: function search(query) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$limit = _ref.limit, + limit = _ref$limit === void 0 ? -1 : _ref$limit; + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + includeScore = _this$options.includeScore, + shouldSort = _this$options.shouldSort, + sortFn = _this$options.sortFn, + ignoreFieldNorm = _this$options.ignoreFieldNorm; + var results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore$1(results, { + ignoreFieldNorm: ignoreFieldNorm + }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches: includeMatches, + includeScore: includeScore + }); + } + }, { + key: "_searchStringList", + value: function _searchStringList(query) { + var searcher = createSearcher(query, this.options); + var records = this._myIndex.records; + var results = []; // Iterate over every string in the index + + records.forEach(function (_ref2) { + var text = _ref2.v, + idx = _ref2.i, + norm = _ref2.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn = searcher.searchIn(text), + isMatch = _searcher$searchIn.isMatch, + score = _searcher$searchIn.score, + indices = _searcher$searchIn.indices; + + if (isMatch) { + results.push({ + item: text, + idx: idx, + matches: [{ + score: score, + value: text, + norm: norm, + indices: indices + }] + }); + } + }); + return results; + } + }, { + key: "_searchLogical", + value: function _searchLogical(query) { + + { + throw new Error(LOGICAL_SEARCH_UNAVAILABLE); + } + } + }, { + key: "_searchObjectList", + value: function _searchObjectList(query) { + var _this2 = this; + + var searcher = createSearcher(query, this.options); + var _this$_myIndex = this._myIndex, + keys = _this$_myIndex.keys, + records = _this$_myIndex.records; + var results = []; // List is Array + + records.forEach(function (_ref5) { + var item = _ref5.$, + idx = _ref5.i; + + if (!isDefined(item)) { + return; + } + + var matches = []; // Iterate over every key (i.e, path), and fetch the value at that key + + keys.forEach(function (key, keyIndex) { + matches.push.apply(matches, _toConsumableArray(_this2._findMatches({ + key: key, + value: item[keyIndex], + searcher: searcher + }))); + }); + + if (matches.length) { + results.push({ + idx: idx, + item: item, + matches: matches + }); + } + }); + return results; + } + }, { + key: "_findMatches", + value: function _findMatches(_ref6) { + var key = _ref6.key, + value = _ref6.value, + searcher = _ref6.searcher; + + if (!isDefined(value)) { + return []; + } + + var matches = []; + + if (isArray(value)) { + value.forEach(function (_ref7) { + var text = _ref7.v, + idx = _ref7.i, + norm = _ref7.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn2 = searcher.searchIn(text), + isMatch = _searcher$searchIn2.isMatch, + score = _searcher$searchIn2.score, + indices = _searcher$searchIn2.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + idx: idx, + norm: norm, + indices: indices + }); + } + }); + } else { + var text = value.v, + norm = value.n; + + var _searcher$searchIn3 = searcher.searchIn(text), + isMatch = _searcher$searchIn3.isMatch, + score = _searcher$searchIn3.score, + indices = _searcher$searchIn3.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + norm: norm, + indices: indices + }); + } + } + + return matches; + } + }]); + + return Fuse; + }(); + + Fuse.version = '6.4.6'; + Fuse.createIndex = createIndex; + Fuse.parseIndex = parseIndex; + Fuse.config = Config; + + { + Fuse.parseQuery = parse; + } + + return Fuse; + +}))); diff --git a/node_modules/fuse.js/dist/fuse.basic.min.js b/node_modules/fuse.js/dist/fuse.basic.min.js new file mode 100644 index 0000000..3ca4e66 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.basic.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var e,t;e=this,t=function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:3,t=new Map,n=Math.pow(10,e);return{get:function(e){var r=e.match(w).length;if(t.has(r))return t.get(r);var i=1/Math.sqrt(r),o=parseFloat(Math.round(i*n)/n);return t.set(r,o),o},clear:function(){t.clear()}}}var _=function(){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.getFn,i=void 0===r?x.getFn:r;t(this,e),this.norm=L(3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return r(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,u(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();u(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?x.getFn:r,o=new _({getFn:i});return o.setKeys(e.map(b)),o.setSources(t),o.create(),o}function O(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,a=t.expectedLocation,c=void 0===a?0:a,s=t.distance,h=void 0===s?x.distance:s,u=t.ignoreLocation,l=void 0===u?x.ignoreLocation:u,d=r/e.length;if(l)return d;var f=Math.abs(c-o);return h?d+f/h:f?1:d}function A(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:x.minMatchCharLength,n=[],r=-1,i=-1,o=0,a=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}function j(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,a=void 0===o?x.location:o,c=i.threshold,s=void 0===c?x.threshold:c,h=i.distance,u=void 0===h?x.distance:h,l=i.includeMatches,d=void 0===l?x.includeMatches:l,f=i.findAllMatches,v=void 0===f?x.findAllMatches:f,y=i.minMatchCharLength,g=void 0===y?x.minMatchCharLength:y,p=i.isCaseSensitive,m=void 0===p?x.isCaseSensitive:p,b=i.ignoreLocation,k=void 0===b?x.ignoreLocation:b;if(t(this,e),this.options={location:a,threshold:s,distance:u,includeMatches:d,findAllMatches:v,minMatchCharLength:g,isCaseSensitive:m,ignoreLocation:k},this.pattern=m?n:n.toLowerCase(),this.chunks=[],this.pattern.length){var M=function(e,t){r.chunks.push({pattern:e,alphabet:j(e),startIndex:t})},w=this.pattern.length;if(w>32){for(var L=0,_=w%32,S=w-_;L3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?x.location:i,a=r.distance,c=void 0===a?x.distance:a,s=r.threshold,h=void 0===s?x.threshold:s,u=r.findAllMatches,l=void 0===u?x.findAllMatches:u,d=r.minMatchCharLength,f=void 0===d?x.minMatchCharLength:d,v=r.includeMatches,y=void 0===v?x.includeMatches:v,p=r.ignoreLocation,m=void 0===p?x.ignoreLocation:p;if(t.length>32)throw new Error(g(32));for(var b,k=t.length,M=e.length,w=Math.max(0,Math.min(o,M)),L=h,_=w,S=f>1||y,j=S?Array(M):[];(b=e.indexOf(t,_))>-1;){var E=O(t,{currentLocation:b,expectedLocation:w,distance:c,ignoreLocation:m});if(L=Math.min(E,L),_=b+k,S)for(var I=0;I=J;U-=1){var q=U-1,B=n[e.charAt(q)];if(S&&(j[q]=+!!B),T[U]=(T[U+1]<<1|1)&B,$&&(T[U]|=(C[U+1]|C[U])<<1|1|C[U+1]),T[U]&N&&(F=O(t,{errors:$,currentLocation:q,expectedLocation:w,distance:c,ignoreLocation:m}))<=L){if(L=F,(_=q)<=w)break;J=Math.max(1,2*w-_)}}var V=O(t,{errors:$+1,currentLocation:w,expectedLocation:w,distance:c,ignoreLocation:m});if(V>L)break;C=T}var G={isMatch:_>=0,score:Math.max(.001,F)};if(S){var H=A(j,f);H.length?y&&(G.indices=H):G.isMatch=!1}return G}(e,n,i,{location:a+o,distance:s,threshold:h,findAllMatches:u,minMatchCharLength:l,includeMatches:r,ignoreLocation:d}),m=p.isMatch,b=p.score,k=p.indices;m&&(y=!0),v+=b,m&&k&&(f=[].concat(c(f),c(k)))}));var p={isMatch:y,score:y?v/this.chunks.length:1};return y&&r&&(p.indices=f),p}}]),e}(),I=[];function C(e,t){for(var n=0,r=I.length;n-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function N(e,t){t.score=e.score}function $(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?x.includeMatches:r,o=n.includeScore,a=void 0===o?x.includeScore:o,c=[];return i&&c.push(P),a&&c.push(N),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return c.length&&c.forEach((function(t){t(e,r)})),r}))}var D=function(){function e(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;if(t(this,e),this.options=a({},x,{},r),this.options.useExtendedSearch)throw new Error("Extended search is not available");this._keyStore=new m(this.options.keys),this.setCollection(n,i)}return r(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof _))throw new Error("Incorrect 'index' type");this._myIndex=t||S(this.options.keys,this._docs,{getFn:this.options.getFn})}},{key:"add",value:function(e){f(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,a=i.includeScore,c=i.shouldSort,s=i.sortFn,h=i.ignoreFieldNorm,d=u(e)?u(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return F(d,{ignoreFieldNorm:h}),c&&d.sort(s),l(r)&&r>-1&&(d=d.slice(0,r)),$(d,this._docs,{includeMatches:o,includeScore:a})}},{key:"_searchStringList",value:function(e){var t=C(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(f(n)){var a=t.searchIn(n),c=a.isMatch,s=a.score,h=a.indices;c&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:h}]})}})),r}},{key:"_searchLogical",value:function(e){throw new Error("Logical search is not available")}},{key:"_searchObjectList",value:function(e){var t=this,n=C(e,this.options),r=this._myIndex,i=r.keys,o=r.records,a=[];return o.forEach((function(e){var r=e.$,o=e.i;if(f(r)){var s=[];i.forEach((function(e,i){s.push.apply(s,c(t._findMatches({key:e,value:r[i],searcher:n})))})),s.length&&a.push({idx:o,item:r,matches:s})}})),a}},{key:"_findMatches",value:function(e){var t=e.key,n=e.value,r=e.searcher;if(!f(n))return[];var i=[];if(h(n))n.forEach((function(e){var n=e.v,o=e.i,a=e.n;if(f(n)){var c=r.searchIn(n),s=c.isMatch,h=c.score,u=c.indices;s&&i.push({score:h,key:t,value:n,idx:o,norm:a,indices:u})}}));else{var o=n.v,a=n.n,c=r.searchIn(o),s=c.isMatch,u=c.score,l=c.indices;s&&i.push({score:u,key:t,value:o,norm:a,indices:l})}return i}}]),e}();return D.version="6.4.6",D.createIndex=S,D.parseIndex=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?x.getFn:n,i=e.keys,o=e.records,a=new _({getFn:r});return a.setKeys(i),a.setIndexRecords(o),a},D.config=x,D},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Fuse=t(); \ No newline at end of file diff --git a/node_modules/fuse.js/dist/fuse.common.js b/node_modules/fuse.js/dist/fuse.common.js new file mode 100644 index 0000000..6a48e1a --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.common.js @@ -0,0 +1,2249 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +'use strict'; + +function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); +} + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} + +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; +} + +function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); +} + +function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); +} + +function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); +} + +function _isNativeReflectConstruct() { + if (typeof Reflect === "undefined" || !Reflect.construct) return false; + if (Reflect.construct.sham) return false; + if (typeof Proxy === "function") return true; + + try { + Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); + return true; + } catch (e) { + return false; + } +} + +function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; +} + +function _possibleConstructorReturn(self, call) { + if (call && (typeof call === "object" || typeof call === "function")) { + return call; + } + + return _assertThisInitialized(self); +} + +function _createSuper(Derived) { + var hasNativeReflectConstruct = _isNativeReflectConstruct(); + + return function _createSuperInternal() { + var Super = _getPrototypeOf(Derived), + result; + + if (hasNativeReflectConstruct) { + var NewTarget = _getPrototypeOf(this).constructor; + + result = Reflect.construct(Super, arguments, NewTarget); + } else { + result = Super.apply(this, arguments); + } + + return _possibleConstructorReturn(this, result); + }; +} + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} + +function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; +} + +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); +} // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js + +var INFINITY = 1 / 0; +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + + var result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; +} +function toString(value) { + return value == null ? '' : baseToString(value); +} +function isString(value) { + return typeof value === 'string'; +} +function isNumber(value) { + return typeof value === 'number'; +} // Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js + +function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; +} +function isObject(value) { + return _typeof(value) === 'object'; +} // Checks if `value` is object-like. + +function isObjectLike(value) { + return isObject(value) && value !== null; +} +function isDefined(value) { + return value !== undefined && value !== null; +} +function isBlank(value) { + return !value.trim().length; +} // Gets the `toStringTag` of `value`. +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js + +function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); +} + +var EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; +var INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; +var LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = function LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key) { + return "Invalid value for key ".concat(key); +}; +var PATTERN_LENGTH_TOO_LARGE = function PATTERN_LENGTH_TOO_LARGE(max) { + return "Pattern length exceeds max of ".concat(max, "."); +}; +var MISSING_KEY_PROPERTY = function MISSING_KEY_PROPERTY(name) { + return "Missing ".concat(name, " property in key"); +}; +var INVALID_KEY_WEIGHT_VALUE = function INVALID_KEY_WEIGHT_VALUE(key) { + return "Property 'weight' in key '".concat(key, "' must be a positive integer"); +}; + +var hasOwn = Object.prototype.hasOwnProperty; + +var KeyStore = /*#__PURE__*/function () { + function KeyStore(keys) { + var _this = this; + + _classCallCheck(this, KeyStore); + + this._keys = []; + this._keyMap = {}; + var totalWeight = 0; + keys.forEach(function (key) { + var obj = createKey(key); + totalWeight += obj.weight; + + _this._keys.push(obj); + + _this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); // Normalize weights so that their sum is equal to 1 + + this._keys.forEach(function (key) { + key.weight /= totalWeight; + }); + } + + _createClass(KeyStore, [{ + key: "get", + value: function get(keyId) { + return this._keyMap[keyId]; + } + }, { + key: "keys", + value: function keys() { + return this._keys; + } + }, { + key: "toJSON", + value: function toJSON() { + return JSON.stringify(this._keys); + } + }]); + + return KeyStore; +}(); +function createKey(key) { + var path = null; + var id = null; + var src = null; + var weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + + var name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { + path: path, + id: id, + weight: weight, + src: src + }; +} +function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); +} +function createKeyId(key) { + return isArray(key) ? key.join('.') : key; +} + +function get(obj, path) { + var list = []; + var arr = false; + + var deepGet = function deepGet(obj, path, index) { + if (!isDefined(obj)) { + return; + } + + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + var key = path[index]; + var value = obj[key]; + + if (!isDefined(value)) { + return; + } // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + + + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; // Search each item in the array. + + for (var i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; // Backwards compatibility (since path used to be a string) + + + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; +} + +var MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 +}; +var BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: function sortFn(a, b) { + return a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1; + } +}; +var FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 +}; +var AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false +}; +var Config = _objectSpread2({}, BasicOptions, {}, MatchOptions, {}, FuzzyOptions, {}, AdvancedOptions); + +var SPACE = /[^ ]+/g; // Field-length norm: the shorter the field, the higher the weight. +// Set to 3 decimals to reduce index size. + +function norm() { + var mantissa = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3; + var cache = new Map(); + var m = Math.pow(10, mantissa); + return { + get: function get(value) { + var numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + var norm = 1 / Math.sqrt(numTokens); // In place of `toFixed(mantissa)`, for faster computation + + var n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear: function clear() { + cache.clear(); + } + }; +} + +var FuseIndex = /*#__PURE__*/function () { + function FuseIndex() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$getFn = _ref.getFn, + getFn = _ref$getFn === void 0 ? Config.getFn : _ref$getFn; + + _classCallCheck(this, FuseIndex); + + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + + _createClass(FuseIndex, [{ + key: "setSources", + value: function setSources() { + var docs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.docs = docs; + } + }, { + key: "setIndexRecords", + value: function setIndexRecords() { + var records = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.records = records; + } + }, { + key: "setKeys", + value: function setKeys() { + var _this = this; + + var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.keys = keys; + this._keysMap = {}; + keys.forEach(function (key, idx) { + _this._keysMap[key.id] = idx; + }); + } + }, { + key: "create", + value: function create() { + var _this2 = this; + + if (this.isCreated || !this.docs.length) { + return; + } + + this.isCreated = true; // List is Array + + if (isString(this.docs[0])) { + this.docs.forEach(function (doc, docIndex) { + _this2._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach(function (doc, docIndex) { + _this2._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } // Adds a doc to the end of the index + + }, { + key: "add", + value: function add(doc) { + var idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } // Removes the doc at the specified index of the index + + }, { + key: "removeAt", + value: function removeAt(idx) { + this.records.splice(idx, 1); // Change ref index of every subsquent doc + + for (var i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + }, { + key: "getValueForItemAtKeyId", + value: function getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + }, { + key: "size", + value: function size() { + return this.records.length; + } + }, { + key: "_addString", + value: function _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + + var record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + }, { + key: "_addObject", + value: function _addObject(doc, docIndex) { + var _this3 = this; + + var record = { + i: docIndex, + $: {} + }; // Iterate over every key (i.e, path), and fetch the value at that key + + this.keys.forEach(function (key, keyIndex) { + // console.log(key) + var value = _this3.getFn(doc, key.path); + + if (!isDefined(value)) { + return; + } + + if (isArray(value)) { + (function () { + var subRecords = []; + var stack = [{ + nestedArrIndex: -1, + value: value + }]; + + while (stack.length) { + var _stack$pop = stack.pop(), + nestedArrIndex = _stack$pop.nestedArrIndex, + _value = _stack$pop.value; + + if (!isDefined(_value)) { + continue; + } + + if (isString(_value) && !isBlank(_value)) { + var subRecord = { + v: _value, + i: nestedArrIndex, + n: _this3.norm.get(_value) + }; + subRecords.push(subRecord); + } else if (isArray(_value)) { + _value.forEach(function (item, k) { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + + record.$[keyIndex] = subRecords; + })(); + } else if (!isBlank(value)) { + var subRecord = { + v: value, + n: _this3.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + }, { + key: "toJSON", + value: function toJSON() { + return { + keys: this.keys, + records: this.records + }; + } + }]); + + return FuseIndex; +}(); +function createIndex(keys, docs) { + var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref2$getFn = _ref2.getFn, + getFn = _ref2$getFn === void 0 ? Config.getFn : _ref2$getFn; + + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; +} +function parseIndex(data) { + var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref3$getFn = _ref3.getFn, + getFn = _ref3$getFn === void 0 ? Config.getFn : _ref3$getFn; + + var keys = data.keys, + records = data.records; + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; +} + +function computeScore(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$errors = _ref.errors, + errors = _ref$errors === void 0 ? 0 : _ref$errors, + _ref$currentLocation = _ref.currentLocation, + currentLocation = _ref$currentLocation === void 0 ? 0 : _ref$currentLocation, + _ref$expectedLocation = _ref.expectedLocation, + expectedLocation = _ref$expectedLocation === void 0 ? 0 : _ref$expectedLocation, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + var accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy; + } + + var proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + + return accuracy + proximity / distance; +} + +function convertMaskToIndices() { + var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Config.minMatchCharLength; + var indices = []; + var start = -1; + var end = -1; + var i = 0; + + for (var len = matchmask.length; i < len; i += 1) { + var match = matchmask[i]; + + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + + start = -1; + } + } // (i-1 - start) + 1 => i - start + + + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices; +} + +// Machine word size +var MAX_BITS = 32; + +function search(text, pattern, patternAlphabet) { + var _ref = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + + var patternLen = pattern.length; // Set starting location at beginning text and initialize the alphabet. + + var textLen = text.length; // Handle the case when location > text.length + + var expectedLocation = Math.max(0, Math.min(location, textLen)); // Highest score beyond which we give up. + + var currentThreshold = threshold; // Is there a nearby exact match? (speedup) + + var bestLocation = expectedLocation; // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + + var computeMatches = minMatchCharLength > 1 || includeMatches; // A mask of the matches, used for building the indices + + var matchMask = computeMatches ? Array(textLen) : []; + var index; // Get all exact matches, here for speed up + + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + var score = computeScore(pattern, { + currentLocation: index, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + var i = 0; + + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } // Reset the best location + + + bestLocation = -1; + var lastBitArr = []; + var finalScore = 1; + var binMax = patternLen + textLen; + var mask = 1 << patternLen - 1; + + for (var _i = 0; _i < patternLen; _i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + var binMin = 0; + var binMid = binMax; + + while (binMin < binMid) { + var _score2 = computeScore(pattern, { + errors: _i, + currentLocation: expectedLocation + binMid, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score2 <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } // Use the result from this iteration as the maximum for the next. + + + binMax = binMid; + var start = Math.max(1, expectedLocation - binMid + 1); + var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; // Initialize the bit array + + var bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << _i) - 1; + + for (var j = finish; j >= start; j -= 1) { + var currentLocation = j - 1; + var charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } // First pass: exact match + + + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; // Subsequent passes: fuzzy match + + if (_i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: _i, + currentLocation: currentLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); // This match will almost certainly be better than any existing match. + // But check anyway. + + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; // Already passed `loc`, downhill from here on in. + + if (bestLocation <= expectedLocation) { + break; + } // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + + + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } // No hope for a (better) match at greater error levels. + + + var _score = computeScore(pattern, { + errors: _i + 1, + currentLocation: expectedLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score > currentThreshold) { + break; + } + + lastBitArr = bitArr; + } + + var result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + var indices = convertMaskToIndices(matchMask, minMatchCharLength); + + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result; +} + +function createPatternAlphabet(pattern) { + var mask = {}; + + for (var i = 0, len = pattern.length; i < len; i += 1) { + var char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + + return mask; +} + +var BitapSearch = /*#__PURE__*/function () { + function BitapSearch(pattern) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, BitapSearch); + + this.options = { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + + if (!this.pattern.length) { + return; + } + + var addChunk = function addChunk(pattern, startIndex) { + _this.chunks.push({ + pattern: pattern, + alphabet: createPatternAlphabet(pattern), + startIndex: startIndex + }); + }; + + var len = this.pattern.length; + + if (len > MAX_BITS) { + var i = 0; + var remainder = len % MAX_BITS; + var end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + var startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + _createClass(BitapSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var _this$options = this.options, + isCaseSensitive = _this$options.isCaseSensitive, + includeMatches = _this$options.includeMatches; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } // Exact match + + + if (this.pattern === text) { + var _result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + _result.indices = [[0, text.length - 1]]; + } + + return _result; + } // Otherwise, use Bitap algorithm + + + var _this$options2 = this.options, + location = _this$options2.location, + distance = _this$options2.distance, + threshold = _this$options2.threshold, + findAllMatches = _this$options2.findAllMatches, + minMatchCharLength = _this$options2.minMatchCharLength, + ignoreLocation = _this$options2.ignoreLocation; + var allIndices = []; + var totalScore = 0; + var hasMatches = false; + this.chunks.forEach(function (_ref2) { + var pattern = _ref2.pattern, + alphabet = _ref2.alphabet, + startIndex = _ref2.startIndex; + + var _search = search(text, pattern, alphabet, { + location: location + startIndex, + distance: distance, + threshold: threshold, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + includeMatches: includeMatches, + ignoreLocation: ignoreLocation + }), + isMatch = _search.isMatch, + score = _search.score, + indices = _search.indices; + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } + }); + var result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result; + } + }]); + + return BitapSearch; +}(); + +var BaseMatch = /*#__PURE__*/function () { + function BaseMatch(pattern) { + _classCallCheck(this, BaseMatch); + + this.pattern = pattern; + } + + _createClass(BaseMatch, [{ + key: "search", + value: function search() + /*text*/ + {} + }], [{ + key: "isMultiMatch", + value: function isMultiMatch(pattern) { + return getMatch(pattern, this.multiRegex); + } + }, { + key: "isSingleMatch", + value: function isSingleMatch(pattern) { + return getMatch(pattern, this.singleRegex); + } + }]); + + return BaseMatch; +}(); + +function getMatch(pattern, exp) { + var matches = pattern.match(exp); + return matches ? matches[1] : null; +} + +var ExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(ExactMatch, _BaseMatch); + + var _super = _createSuper(ExactMatch); + + function ExactMatch(pattern) { + _classCallCheck(this, ExactMatch); + + return _super.call(this, pattern); + } + + _createClass(ExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text === this.pattern; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^="(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^=(.*)$/; + } + }]); + + return ExactMatch; +}(BaseMatch); + +var InverseExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InverseExactMatch, _BaseMatch); + + var _super = _createSuper(InverseExactMatch); + + function InverseExactMatch(pattern) { + _classCallCheck(this, InverseExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InverseExactMatch, [{ + key: "search", + value: function search(text) { + var index = text.indexOf(this.pattern); + var isMatch = index === -1; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!(.*)$/; + } + }]); + + return InverseExactMatch; +}(BaseMatch); + +var PrefixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(PrefixExactMatch, _BaseMatch); + + var _super = _createSuper(PrefixExactMatch); + + function PrefixExactMatch(pattern) { + _classCallCheck(this, PrefixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(PrefixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text.startsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'prefix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^\^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^\^(.*)$/; + } + }]); + + return PrefixExactMatch; +}(BaseMatch); + +var InversePrefixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InversePrefixExactMatch, _BaseMatch); + + var _super = _createSuper(InversePrefixExactMatch); + + function InversePrefixExactMatch(pattern) { + _classCallCheck(this, InversePrefixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InversePrefixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = !text.startsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-prefix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!\^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!\^(.*)$/; + } + }]); + + return InversePrefixExactMatch; +}(BaseMatch); + +var SuffixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(SuffixExactMatch, _BaseMatch); + + var _super = _createSuper(SuffixExactMatch); + + function SuffixExactMatch(pattern) { + _classCallCheck(this, SuffixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(SuffixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text.endsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [text.length - this.pattern.length, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'suffix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^"(.*)"\$$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^(.*)\$$/; + } + }]); + + return SuffixExactMatch; +}(BaseMatch); + +var InverseSuffixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InverseSuffixExactMatch, _BaseMatch); + + var _super = _createSuper(InverseSuffixExactMatch); + + function InverseSuffixExactMatch(pattern) { + _classCallCheck(this, InverseSuffixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InverseSuffixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = !text.endsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-suffix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!"(.*)"\$$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!(.*)\$$/; + } + }]); + + return InverseSuffixExactMatch; +}(BaseMatch); + +var FuzzyMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(FuzzyMatch, _BaseMatch); + + var _super = _createSuper(FuzzyMatch); + + function FuzzyMatch(pattern) { + var _this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, FuzzyMatch); + + _this = _super.call(this, pattern); + _this._bitapSearch = new BitapSearch(pattern, { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }); + return _this; + } + + _createClass(FuzzyMatch, [{ + key: "search", + value: function search(text) { + return this._bitapSearch.searchIn(text); + } + }], [{ + key: "type", + get: function get() { + return 'fuzzy'; + } + }, { + key: "multiRegex", + get: function get() { + return /^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^(.*)$/; + } + }]); + + return FuzzyMatch; +}(BaseMatch); + +var IncludeMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(IncludeMatch, _BaseMatch); + + var _super = _createSuper(IncludeMatch); + + function IncludeMatch(pattern) { + _classCallCheck(this, IncludeMatch); + + return _super.call(this, pattern); + } + + _createClass(IncludeMatch, [{ + key: "search", + value: function search(text) { + var location = 0; + var index; + var indices = []; + var patternLen = this.pattern.length; // Get all exact matches + + while ((index = text.indexOf(this.pattern, location)) > -1) { + location = index + patternLen; + indices.push([index, location - 1]); + } + + var isMatch = !!indices.length; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: indices + }; + } + }], [{ + key: "type", + get: function get() { + return 'include'; + } + }, { + key: "multiRegex", + get: function get() { + return /^'"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^'(.*)$/; + } + }]); + + return IncludeMatch; +}(BaseMatch); + +var searchers = [ExactMatch, IncludeMatch, PrefixExactMatch, InversePrefixExactMatch, InverseSuffixExactMatch, SuffixExactMatch, InverseExactMatch, FuzzyMatch]; +var searchersLen = searchers.length; // Regex to split by spaces, but keep anything in quotes together + +var SPACE_RE = / +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/; +var OR_TOKEN = '|'; // Return a 2D array representation of the query, for simpler parsing. +// Example: +// "^core go$ | rb$ | py$ xy$" => [["^core", "go$"], ["rb$"], ["py$", "xy$"]] + +function parseQuery(pattern) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + return pattern.split(OR_TOKEN).map(function (item) { + var query = item.trim().split(SPACE_RE).filter(function (item) { + return item && !!item.trim(); + }); + var results = []; + + for (var i = 0, len = query.length; i < len; i += 1) { + var queryItem = query[i]; // 1. Handle multiple query match (i.e, once that are quoted, like `"hello world"`) + + var found = false; + var idx = -1; + + while (!found && ++idx < searchersLen) { + var searcher = searchers[idx]; + var token = searcher.isMultiMatch(queryItem); + + if (token) { + results.push(new searcher(token, options)); + found = true; + } + } + + if (found) { + continue; + } // 2. Handle single query matches (i.e, once that are *not* quoted) + + + idx = -1; + + while (++idx < searchersLen) { + var _searcher = searchers[idx]; + + var _token = _searcher.isSingleMatch(queryItem); + + if (_token) { + results.push(new _searcher(_token, options)); + break; + } + } + } + + return results; + }); +} + +// to a singl match + +var MultiMatchSet = new Set([FuzzyMatch.type, IncludeMatch.type]); +/** + * Command-like searching + * ====================== + * + * Given multiple search terms delimited by spaces.e.g. `^jscript .python$ ruby !java`, + * search in a given text. + * + * Search syntax: + * + * | Token | Match type | Description | + * | ----------- | -------------------------- | -------------------------------------- | + * | `jscript` | fuzzy-match | Items that fuzzy match `jscript` | + * | `=scheme` | exact-match | Items that are `scheme` | + * | `'python` | include-match | Items that include `python` | + * | `!ruby` | inverse-exact-match | Items that do not include `ruby` | + * | `^java` | prefix-exact-match | Items that start with `java` | + * | `!^earlang` | inverse-prefix-exact-match | Items that do not start with `earlang` | + * | `.js$` | suffix-exact-match | Items that end with `.js` | + * | `!.go$` | inverse-suffix-exact-match | Items that do not end with `.go` | + * + * A single pipe character acts as an OR operator. For example, the following + * query matches entries that start with `core` and end with either`go`, `rb`, + * or`py`. + * + * ``` + * ^core go$ | rb$ | py$ + * ``` + */ + +var ExtendedSearch = /*#__PURE__*/function () { + function ExtendedSearch(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance; + + _classCallCheck(this, ExtendedSearch); + + this.query = null; + this.options = { + isCaseSensitive: isCaseSensitive, + includeMatches: includeMatches, + minMatchCharLength: minMatchCharLength, + findAllMatches: findAllMatches, + ignoreLocation: ignoreLocation, + location: location, + threshold: threshold, + distance: distance + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.query = parseQuery(this.pattern, this.options); + } + + _createClass(ExtendedSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var query = this.query; + + if (!query) { + return { + isMatch: false, + score: 1 + }; + } + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + isCaseSensitive = _this$options.isCaseSensitive; + text = isCaseSensitive ? text : text.toLowerCase(); + var numMatches = 0; + var allIndices = []; + var totalScore = 0; // ORs + + for (var i = 0, qLen = query.length; i < qLen; i += 1) { + var searchers = query[i]; // Reset indices + + allIndices.length = 0; + numMatches = 0; // ANDs + + for (var j = 0, pLen = searchers.length; j < pLen; j += 1) { + var searcher = searchers[j]; + + var _searcher$search = searcher.search(text), + isMatch = _searcher$search.isMatch, + indices = _searcher$search.indices, + score = _searcher$search.score; + + if (isMatch) { + numMatches += 1; + totalScore += score; + + if (includeMatches) { + var type = searcher.constructor.type; + + if (MultiMatchSet.has(type)) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } else { + allIndices.push(indices); + } + } + } else { + totalScore = 0; + numMatches = 0; + allIndices.length = 0; + break; + } + } // OR condition, so if TRUE, return + + + if (numMatches) { + var result = { + isMatch: true, + score: totalScore / numMatches + }; + + if (includeMatches) { + result.indices = allIndices; + } + + return result; + } + } // Nothing was matched + + + return { + isMatch: false, + score: 1 + }; + } + }], [{ + key: "condition", + value: function condition(_, options) { + return options.useExtendedSearch; + } + }]); + + return ExtendedSearch; +}(); + +var registeredSearchers = []; +function register() { + registeredSearchers.push.apply(registeredSearchers, arguments); +} +function createSearcher(pattern, options) { + for (var i = 0, len = registeredSearchers.length; i < len; i += 1) { + var searcherClass = registeredSearchers[i]; + + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + + return new BitapSearch(pattern, options); +} + +var LogicalOperator = { + AND: '$and', + OR: '$or' +}; +var KeyType = { + PATH: '$path', + PATTERN: '$val' +}; + +var isExpression = function isExpression(query) { + return !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); +}; + +var isPath = function isPath(query) { + return !!query[KeyType.PATH]; +}; + +var isLeaf = function isLeaf(query) { + return !isArray(query) && isObject(query) && !isExpression(query); +}; + +var convertToExplicit = function convertToExplicit(query) { + return _defineProperty({}, LogicalOperator.AND, Object.keys(query).map(function (key) { + return _defineProperty({}, key, query[key]); + })); +}; // When `auto` is `true`, the parse function will infer and initialize and add +// the appropriate `Searcher` instance + + +function parse(query, options) { + var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref3$auto = _ref3.auto, + auto = _ref3$auto === void 0 ? true : _ref3$auto; + + var next = function next(query) { + var keys = Object.keys(query); + var isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + + if (isLeaf(query)) { + var key = isQueryPath ? query[KeyType.PATH] : keys[0]; + var pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + + var obj = { + keyId: createKeyId(key), + pattern: pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj; + } + + var node = { + children: [], + operator: keys[0] + }; + keys.forEach(function (key) { + var value = query[key]; + + if (isArray(value)) { + value.forEach(function (item) { + node.children.push(next(item)); + }); + } + }); + return node; + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query); +} + +function computeScore$1(results, _ref) { + var _ref$ignoreFieldNorm = _ref.ignoreFieldNorm, + ignoreFieldNorm = _ref$ignoreFieldNorm === void 0 ? Config.ignoreFieldNorm : _ref$ignoreFieldNorm; + results.forEach(function (result) { + var totalScore = 1; + result.matches.forEach(function (_ref2) { + var key = _ref2.key, + norm = _ref2.norm, + score = _ref2.score; + var weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); +} + +function transformMatches(result, data) { + var matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return; + } + + matches.forEach(function (match) { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + + var indices = match.indices, + value = match.value; + var obj = { + indices: indices, + value: value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); +} + +function transformScore(result, data) { + data.score = result.score; +} + +function format(results, docs) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$includeScore = _ref.includeScore, + includeScore = _ref$includeScore === void 0 ? Config.includeScore : _ref$includeScore; + + var transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(function (result) { + var idx = result.idx; + var data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach(function (transformer) { + transformer(result, data); + }); + } + + return data; + }); +} + +var Fuse = /*#__PURE__*/function () { + function Fuse(docs) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var index = arguments.length > 2 ? arguments[2] : undefined; + + _classCallCheck(this, Fuse); + + this.options = _objectSpread2({}, Config, {}, options); + + if (this.options.useExtendedSearch && !true) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + + _createClass(Fuse, [{ + key: "setCollection", + value: function setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + }, { + key: "add", + value: function add(doc) { + if (!isDefined(doc)) { + return; + } + + this._docs.push(doc); + + this._myIndex.add(doc); + } + }, { + key: "remove", + value: function remove() { + var predicate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { + return ( + /* doc, idx */ + false + ); + }; + var results = []; + + for (var i = 0, len = this._docs.length; i < len; i += 1) { + var doc = this._docs[i]; + + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + + return results; + } + }, { + key: "removeAt", + value: function removeAt(idx) { + this._docs.splice(idx, 1); + + this._myIndex.removeAt(idx); + } + }, { + key: "getIndex", + value: function getIndex() { + return this._myIndex; + } + }, { + key: "search", + value: function search(query) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$limit = _ref.limit, + limit = _ref$limit === void 0 ? -1 : _ref$limit; + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + includeScore = _this$options.includeScore, + shouldSort = _this$options.shouldSort, + sortFn = _this$options.sortFn, + ignoreFieldNorm = _this$options.ignoreFieldNorm; + var results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore$1(results, { + ignoreFieldNorm: ignoreFieldNorm + }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches: includeMatches, + includeScore: includeScore + }); + } + }, { + key: "_searchStringList", + value: function _searchStringList(query) { + var searcher = createSearcher(query, this.options); + var records = this._myIndex.records; + var results = []; // Iterate over every string in the index + + records.forEach(function (_ref2) { + var text = _ref2.v, + idx = _ref2.i, + norm = _ref2.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn = searcher.searchIn(text), + isMatch = _searcher$searchIn.isMatch, + score = _searcher$searchIn.score, + indices = _searcher$searchIn.indices; + + if (isMatch) { + results.push({ + item: text, + idx: idx, + matches: [{ + score: score, + value: text, + norm: norm, + indices: indices + }] + }); + } + }); + return results; + } + }, { + key: "_searchLogical", + value: function _searchLogical(query) { + var _this = this; + + var expression = parse(query, this.options); + + var evaluate = function evaluate(node, item, idx) { + if (!node.children) { + var keyId = node.keyId, + searcher = node.searcher; + + var matches = _this._findMatches({ + key: _this._keyStore.get(keyId), + value: _this._myIndex.getValueForItemAtKeyId(item, keyId), + searcher: searcher + }); + + if (matches && matches.length) { + return [{ + idx: idx, + item: item, + matches: matches + }]; + } + + return []; + } + /*eslint indent: [2, 2, {"SwitchCase": 1}]*/ + + + switch (node.operator) { + case LogicalOperator.AND: + { + var res = []; + + for (var i = 0, len = node.children.length; i < len; i += 1) { + var child = node.children[i]; + var result = evaluate(child, item, idx); + + if (result.length) { + res.push.apply(res, _toConsumableArray(result)); + } else { + return []; + } + } + + return res; + } + + case LogicalOperator.OR: + { + var _res = []; + + for (var _i = 0, _len = node.children.length; _i < _len; _i += 1) { + var _child = node.children[_i]; + + var _result = evaluate(_child, item, idx); + + if (_result.length) { + _res.push.apply(_res, _toConsumableArray(_result)); + + break; + } + } + + return _res; + } + } + }; + + var records = this._myIndex.records; + var resultMap = {}; + var results = []; + records.forEach(function (_ref3) { + var item = _ref3.$, + idx = _ref3.i; + + if (isDefined(item)) { + var expResults = evaluate(expression, item, idx); + + if (expResults.length) { + // Dedupe when adding + if (!resultMap[idx]) { + resultMap[idx] = { + idx: idx, + item: item, + matches: [] + }; + results.push(resultMap[idx]); + } + + expResults.forEach(function (_ref4) { + var _resultMap$idx$matche; + + var matches = _ref4.matches; + + (_resultMap$idx$matche = resultMap[idx].matches).push.apply(_resultMap$idx$matche, _toConsumableArray(matches)); + }); + } + } + }); + return results; + } + }, { + key: "_searchObjectList", + value: function _searchObjectList(query) { + var _this2 = this; + + var searcher = createSearcher(query, this.options); + var _this$_myIndex = this._myIndex, + keys = _this$_myIndex.keys, + records = _this$_myIndex.records; + var results = []; // List is Array + + records.forEach(function (_ref5) { + var item = _ref5.$, + idx = _ref5.i; + + if (!isDefined(item)) { + return; + } + + var matches = []; // Iterate over every key (i.e, path), and fetch the value at that key + + keys.forEach(function (key, keyIndex) { + matches.push.apply(matches, _toConsumableArray(_this2._findMatches({ + key: key, + value: item[keyIndex], + searcher: searcher + }))); + }); + + if (matches.length) { + results.push({ + idx: idx, + item: item, + matches: matches + }); + } + }); + return results; + } + }, { + key: "_findMatches", + value: function _findMatches(_ref6) { + var key = _ref6.key, + value = _ref6.value, + searcher = _ref6.searcher; + + if (!isDefined(value)) { + return []; + } + + var matches = []; + + if (isArray(value)) { + value.forEach(function (_ref7) { + var text = _ref7.v, + idx = _ref7.i, + norm = _ref7.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn2 = searcher.searchIn(text), + isMatch = _searcher$searchIn2.isMatch, + score = _searcher$searchIn2.score, + indices = _searcher$searchIn2.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + idx: idx, + norm: norm, + indices: indices + }); + } + }); + } else { + var text = value.v, + norm = value.n; + + var _searcher$searchIn3 = searcher.searchIn(text), + isMatch = _searcher$searchIn3.isMatch, + score = _searcher$searchIn3.score, + indices = _searcher$searchIn3.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + norm: norm, + indices: indices + }); + } + } + + return matches; + } + }]); + + return Fuse; +}(); + +Fuse.version = '6.4.6'; +Fuse.createIndex = createIndex; +Fuse.parseIndex = parseIndex; +Fuse.config = Config; + +{ + Fuse.parseQuery = parse; +} + +{ + register(ExtendedSearch); +} + +module.exports = Fuse; diff --git a/node_modules/fuse.js/dist/fuse.d.ts b/node_modules/fuse.js/dist/fuse.d.ts new file mode 100644 index 0000000..2f537ae --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.d.ts @@ -0,0 +1,293 @@ +// Type definitions for Fuse.js v6.4.6 +// TypeScript v3.9.5 + +export default Fuse +export as namespace Fuse + +declare class Fuse { + constructor( + list: ReadonlyArray, + options?: Fuse.IFuseOptions, + index?: Fuse.FuseIndex + ) + /** + * Search function for the Fuse instance. + * + * ```typescript + * const list: MyType[] = [myType1, myType2, etc...] + + * const options: Fuse.IFuseOptions = { + * keys: ['key1', 'key2'] + * } + * + * const myFuse = new Fuse(list, options) + * let result = myFuse.search('pattern') + * ``` + * + * @param pattern The pattern to search + * @param options `Fuse.FuseSearchOptions` + * @returns An array of search results + */ + search( + pattern: string | Fuse.Expression, + options?: Fuse.FuseSearchOptions + ): Fuse.FuseResult[] + + setCollection(docs: ReadonlyArray, index?: Fuse.FuseIndex): void + + /** + * Adds a doc to the end the list. + */ + add(doc: T): void + + /** + * Removes all documents from the list which the predicate returns truthy for, + * and returns an array of the removed docs. + * The predicate is invoked with two arguments: (doc, index). + */ + remove(predicate: (doc: T, idx: number) => boolean): T[] + + /** + * Removes the doc at the specified index. + */ + removeAt(idx: number): void + + /** + * Returns the generated Fuse index + */ + getIndex(): Fuse.FuseIndex + + /** + * Return the current version. + */ + static version: string + + /** + * Use this method to pre-generate the index from the list, and pass it + * directly into the Fuse instance. + * + * _Note that Fuse will automatically index the table if one isn't provided + * during instantiation._ + * + * ```typescript + * const list: MyType[] = [myType1, myType2, etc...] + * + * const index = Fuse.createIndex( + * keys: ['key1', 'key2'] + * list: list + * ) + * + * const options: Fuse.IFuseOptions = { + * keys: ['key1', 'key2'] + * } + * + * const myFuse = new Fuse(list, options, index) + * ``` + * @param keys The keys to index + * @param list The list from which to create an index + * @param options? + * @returns An indexed list + */ + static createIndex( + keys: Array, + list: ReadonlyArray, + options?: Fuse.FuseIndexOptions + ): Fuse.FuseIndex + + static parseIndex( + index: any, + options?: Fuse.FuseIndexOptions + ): Fuse.FuseIndex +} + +declare namespace Fuse { + export class FuseIndex { + constructor(options?: FuseIndexOptions) + setSources(docs: ReadonlyArray): void + setKeys(keys: ReadonlyArray): void + setIndexRecords(records: FuseIndexRecords): void + create(): void + add(doc: T): void + toJSON(): { + keys: ReadonlyArray + collection: FuseIndexRecords + } + } + + type FuseGetFunction = ( + obj: T, + path: string | string[] + ) => ReadonlyArray | string + + export type FuseIndexOptions = { + getFn: FuseGetFunction + } + + // { + // title: { '$': "Old Man's War" }, + // 'author.firstName': { '$': 'Codenar' } + // } + // + // OR + // + // { + // tags: [ + // { $: 'nonfiction', idx: 0 }, + // { $: 'web development', idx: 1 }, + // ] + // } + export type FuseSortFunctionItem = { + [key: string]: { $: string } | { $: string; idx: number }[] + } + + // { + // score: 0.001, + // key: 'author.firstName', + // value: 'Codenar', + // indices: [ [ 0, 3 ] ] + // } + export type FuseSortFunctionMatch = { + score: number + key: string + value: string + indices: ReadonlyArray[] + } + + // { + // score: 0, + // key: 'tags', + // value: 'nonfiction', + // idx: 1, + // indices: [ [ 0, 9 ] ] + // } + export type FuseSortFunctionMatchList = FuseSortFunctionMatch & { + idx: number + } + + export type FuseSortFunctionArg = { + idx: number + item: FuseSortFunctionItem + score: number + matches?: (FuseSortFunctionMatch | FuseSortFunctionMatchList)[] + } + + export type FuseSortFunction = ( + a: FuseSortFunctionArg, + b: FuseSortFunctionArg + ) => number + + // title: { + // '$': "Old Man's War", + // 'n': 0.5773502691896258 + // } + type RecordEntryObject = { + v: string // The text value + n: number // The field-length norm + } + + // 'author.tags.name': [{ + // 'v': 'pizza lover', + // 'i': 2, + // 'n: 0.7071067811865475 + // } + type RecordEntryArrayItem = ReadonlyArray + + // TODO: this makes it difficult to infer the type. Need to think more about this + type RecordEntry = { [key: string]: RecordEntryObject | RecordEntryArrayItem } + + // { + // i: 0, + // '$': { + // '0': { v: "Old Man's War", n: 0.5773502691896258 }, + // '1': { v: 'Codenar', n: 1 }, + // '2': [ + // { v: 'pizza lover', i: 2, n: 0.7071067811865475 }, + // { v: 'helo wold', i: 1, n: 0.7071067811865475 }, + // { v: 'hello world', i: 0, n: 0.7071067811865475 } + // ] + // } + // } + type FuseIndexObjectRecord = { + i: number // The index of the record in the source list + $: RecordEntry + } + + // { + // keys: null, + // list: [ + // { v: 'one', i: 0, n: 1 }, + // { v: 'two', i: 1, n: 1 }, + // { v: 'three', i: 2, n: 1 } + // ] + // } + type FuseIndexStringRecord = { + i: number // The index of the record in the source list + v: string // The text value + n: number // The field-length norm + } + + type FuseIndexRecords = + | ReadonlyArray + | ReadonlyArray + + // { + // name: 'title', + // weight: 0.7 + // } + export type FuseOptionKeyObject = { + name: string | string[] + weight: number + } + + export type FuseOptionKey = FuseOptionKeyObject | string | string[] + + export interface IFuseOptions { + isCaseSensitive?: boolean + distance?: number + findAllMatches?: boolean + getFn?: FuseGetFunction + ignoreLocation?: boolean + ignoreFieldNorm?: boolean + includeMatches?: boolean + includeScore?: boolean + keys?: Array + location?: number + minMatchCharLength?: number + shouldSort?: boolean + sortFn?: FuseSortFunction + threshold?: number + useExtendedSearch?: boolean + } + + // Denotes the start/end indices of a match + // start end + // ↓ ↓ + type RangeTuple = [number, number] + + export type FuseResultMatch = { + indices: ReadonlyArray + key?: string + refIndex?: number + value?: string + } + + export type FuseSearchOptions = { + limit: number + } + + export type FuseResult = { + item: T + refIndex: number + score?: number + matches?: ReadonlyArray + } + + export type Expression = + | { [key: string]: string } + | { + $path: ReadonlyArray + $val: string + } + | { $and?: Expression[] } + | { $or?: Expression[] } +} diff --git a/node_modules/fuse.js/dist/fuse.esm.js b/node_modules/fuse.js/dist/fuse.esm.js new file mode 100644 index 0000000..c5bfefd --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.esm.js @@ -0,0 +1,1782 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +function isArray(value) { + return !Array.isArray + ? getTag(value) === '[object Array]' + : Array.isArray(value) +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js +const INFINITY = 1 / 0; +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value + } + let result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result +} + +function toString(value) { + return value == null ? '' : baseToString(value) +} + +function isString(value) { + return typeof value === 'string' +} + +function isNumber(value) { + return typeof value === 'number' +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js +function isBoolean(value) { + return ( + value === true || + value === false || + (isObjectLike(value) && getTag(value) == '[object Boolean]') + ) +} + +function isObject(value) { + return typeof value === 'object' +} + +// Checks if `value` is object-like. +function isObjectLike(value) { + return isObject(value) && value !== null +} + +function isDefined(value) { + return value !== undefined && value !== null +} + +function isBlank(value) { + return !value.trim().length +} + +// Gets the `toStringTag` of `value`. +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js +function getTag(value) { + return value == null + ? value === undefined + ? '[object Undefined]' + : '[object Null]' + : Object.prototype.toString.call(value) +} + +const EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; + +const INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; + +const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = (key) => + `Invalid value for key ${key}`; + +const PATTERN_LENGTH_TOO_LARGE = (max) => + `Pattern length exceeds max of ${max}.`; + +const MISSING_KEY_PROPERTY = (name) => `Missing ${name} property in key`; + +const INVALID_KEY_WEIGHT_VALUE = (key) => + `Property 'weight' in key '${key}' must be a positive integer`; + +const hasOwn = Object.prototype.hasOwnProperty; + +class KeyStore { + constructor(keys) { + this._keys = []; + this._keyMap = {}; + + let totalWeight = 0; + + keys.forEach((key) => { + let obj = createKey(key); + + totalWeight += obj.weight; + + this._keys.push(obj); + this._keyMap[obj.id] = obj; + + totalWeight += obj.weight; + }); + + // Normalize weights so that their sum is equal to 1 + this._keys.forEach((key) => { + key.weight /= totalWeight; + }); + } + get(keyId) { + return this._keyMap[keyId] + } + keys() { + return this._keys + } + toJSON() { + return JSON.stringify(this._keys) + } +} + +function createKey(key) { + let path = null; + let id = null; + let src = null; + let weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')) + } + + const name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)) + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { path, id, weight, src } +} + +function createKeyPath(key) { + return isArray(key) ? key : key.split('.') +} + +function createKeyId(key) { + return isArray(key) ? key.join('.') : key +} + +function get(obj, path) { + let list = []; + let arr = false; + + const deepGet = (obj, path, index) => { + if (!isDefined(obj)) { + return + } + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + let key = path[index]; + + const value = obj[key]; + + if (!isDefined(value)) { + return + } + + // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + if ( + index === path.length - 1 && + (isString(value) || isNumber(value) || isBoolean(value)) + ) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; + // Search each item in the array. + for (let i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; + + // Backwards compatibility (since path used to be a string) + deepGet(obj, isString(path) ? path.split('.') : path, 0); + + return arr ? list : list[0] +} + +const MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 +}; + +const BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: (a, b) => + a.score === b.score ? (a.idx < b.idx ? -1 : 1) : a.score < b.score ? -1 : 1 +}; + +const FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 +}; + +const AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false +}; + +var Config = { + ...BasicOptions, + ...MatchOptions, + ...FuzzyOptions, + ...AdvancedOptions +}; + +const SPACE = /[^ ]+/g; + +// Field-length norm: the shorter the field, the higher the weight. +// Set to 3 decimals to reduce index size. +function norm(mantissa = 3) { + const cache = new Map(); + const m = Math.pow(10, mantissa); + + return { + get(value) { + const numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens) + } + + const norm = 1 / Math.sqrt(numTokens); + + // In place of `toFixed(mantissa)`, for faster computation + const n = parseFloat(Math.round(norm * m) / m); + + cache.set(numTokens, n); + + return n + }, + clear() { + cache.clear(); + } + } +} + +class FuseIndex { + constructor({ getFn = Config.getFn } = {}) { + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + + this.setIndexRecords(); + } + setSources(docs = []) { + this.docs = docs; + } + setIndexRecords(records = []) { + this.records = records; + } + setKeys(keys = []) { + this.keys = keys; + this._keysMap = {}; + keys.forEach((key, idx) => { + this._keysMap[key.id] = idx; + }); + } + create() { + if (this.isCreated || !this.docs.length) { + return + } + + this.isCreated = true; + + // List is Array + if (isString(this.docs[0])) { + this.docs.forEach((doc, docIndex) => { + this._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach((doc, docIndex) => { + this._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } + // Adds a doc to the end of the index + add(doc) { + const idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } + // Removes the doc at the specified index of the index + removeAt(idx) { + this.records.splice(idx, 1); + + // Change ref index of every subsquent doc + for (let i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]] + } + size() { + return this.records.length + } + _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return + } + + let record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + + this.records.push(record); + } + _addObject(doc, docIndex) { + let record = { i: docIndex, $: {} }; + + // Iterate over every key (i.e, path), and fetch the value at that key + this.keys.forEach((key, keyIndex) => { + // console.log(key) + let value = this.getFn(doc, key.path); + + if (!isDefined(value)) { + return + } + + if (isArray(value)) { + let subRecords = []; + const stack = [{ nestedArrIndex: -1, value }]; + + while (stack.length) { + const { nestedArrIndex, value } = stack.pop(); + + if (!isDefined(value)) { + continue + } + + if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + i: nestedArrIndex, + n: this.norm.get(value) + }; + + subRecords.push(subRecord); + } else if (isArray(value)) { + value.forEach((item, k) => { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + record.$[keyIndex] = subRecords; + } else if (!isBlank(value)) { + let subRecord = { + v: value, + n: this.norm.get(value) + }; + + record.$[keyIndex] = subRecord; + } + }); + + this.records.push(record); + } + toJSON() { + return { + keys: this.keys, + records: this.records + } + } +} + +function createIndex(keys, docs, { getFn = Config.getFn } = {}) { + const myIndex = new FuseIndex({ getFn }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex +} + +function parseIndex(data, { getFn = Config.getFn } = {}) { + const { keys, records } = data; + const myIndex = new FuseIndex({ getFn }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex +} + +function computeScore( + pattern, + { + errors = 0, + currentLocation = 0, + expectedLocation = 0, + distance = Config.distance, + ignoreLocation = Config.ignoreLocation + } = {} +) { + const accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy + } + + const proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy + } + + return accuracy + proximity / distance +} + +function convertMaskToIndices( + matchmask = [], + minMatchCharLength = Config.minMatchCharLength +) { + let indices = []; + let start = -1; + let end = -1; + let i = 0; + + for (let len = matchmask.length; i < len; i += 1) { + let match = matchmask[i]; + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + start = -1; + } + } + + // (i-1 - start) + 1 => i - start + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices +} + +// Machine word size +const MAX_BITS = 32; + +function search( + text, + pattern, + patternAlphabet, + { + location = Config.location, + distance = Config.distance, + threshold = Config.threshold, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + includeMatches = Config.includeMatches, + ignoreLocation = Config.ignoreLocation + } = {} +) { + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)) + } + + const patternLen = pattern.length; + // Set starting location at beginning text and initialize the alphabet. + const textLen = text.length; + // Handle the case when location > text.length + const expectedLocation = Math.max(0, Math.min(location, textLen)); + // Highest score beyond which we give up. + let currentThreshold = threshold; + // Is there a nearby exact match? (speedup) + let bestLocation = expectedLocation; + + // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + const computeMatches = minMatchCharLength > 1 || includeMatches; + // A mask of the matches, used for building the indices + const matchMask = computeMatches ? Array(textLen) : []; + + let index; + + // Get all exact matches, here for speed up + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + let score = computeScore(pattern, { + currentLocation: index, + expectedLocation, + distance, + ignoreLocation + }); + + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + let i = 0; + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } + + // Reset the best location + bestLocation = -1; + + let lastBitArr = []; + let finalScore = 1; + let binMax = patternLen + textLen; + + const mask = 1 << (patternLen - 1); + + for (let i = 0; i < patternLen; i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + let binMin = 0; + let binMid = binMax; + + while (binMin < binMid) { + const score = computeScore(pattern, { + errors: i, + currentLocation: expectedLocation + binMid, + expectedLocation, + distance, + ignoreLocation + }); + + if (score <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } + + // Use the result from this iteration as the maximum for the next. + binMax = binMid; + + let start = Math.max(1, expectedLocation - binMid + 1); + let finish = findAllMatches + ? textLen + : Math.min(expectedLocation + binMid, textLen) + patternLen; + + // Initialize the bit array + let bitArr = Array(finish + 2); + + bitArr[finish + 1] = (1 << i) - 1; + + for (let j = finish; j >= start; j -= 1) { + let currentLocation = j - 1; + let charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } + + // First pass: exact match + bitArr[j] = ((bitArr[j + 1] << 1) | 1) & charMatch; + + // Subsequent passes: fuzzy match + if (i) { + bitArr[j] |= + ((lastBitArr[j + 1] | lastBitArr[j]) << 1) | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: i, + currentLocation, + expectedLocation, + distance, + ignoreLocation + }); + + // This match will almost certainly be better than any existing match. + // But check anyway. + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; + + // Already passed `loc`, downhill from here on in. + if (bestLocation <= expectedLocation) { + break + } + + // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } + + // No hope for a (better) match at greater error levels. + const score = computeScore(pattern, { + errors: i + 1, + currentLocation: expectedLocation, + expectedLocation, + distance, + ignoreLocation + }); + + if (score > currentThreshold) { + break + } + + lastBitArr = bitArr; + } + + const result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + const indices = convertMaskToIndices(matchMask, minMatchCharLength); + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result +} + +function createPatternAlphabet(pattern) { + let mask = {}; + + for (let i = 0, len = pattern.length; i < len; i += 1) { + const char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | (1 << (len - i - 1)); + } + + return mask +} + +class BitapSearch { + constructor( + pattern, + { + location = Config.location, + threshold = Config.threshold, + distance = Config.distance, + includeMatches = Config.includeMatches, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + isCaseSensitive = Config.isCaseSensitive, + ignoreLocation = Config.ignoreLocation + } = {} + ) { + this.options = { + location, + threshold, + distance, + includeMatches, + findAllMatches, + minMatchCharLength, + isCaseSensitive, + ignoreLocation + }; + + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + + this.chunks = []; + + if (!this.pattern.length) { + return + } + + const addChunk = (pattern, startIndex) => { + this.chunks.push({ + pattern, + alphabet: createPatternAlphabet(pattern), + startIndex + }); + }; + + const len = this.pattern.length; + + if (len > MAX_BITS) { + let i = 0; + const remainder = len % MAX_BITS; + const end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + const startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + searchIn(text) { + const { isCaseSensitive, includeMatches } = this.options; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } + + // Exact match + if (this.pattern === text) { + let result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + result.indices = [[0, text.length - 1]]; + } + + return result + } + + // Otherwise, use Bitap algorithm + const { + location, + distance, + threshold, + findAllMatches, + minMatchCharLength, + ignoreLocation + } = this.options; + + let allIndices = []; + let totalScore = 0; + let hasMatches = false; + + this.chunks.forEach(({ pattern, alphabet, startIndex }) => { + const { isMatch, score, indices } = search(text, pattern, alphabet, { + location: location + startIndex, + distance, + threshold, + findAllMatches, + minMatchCharLength, + includeMatches, + ignoreLocation + }); + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [...allIndices, ...indices]; + } + }); + + let result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result + } +} + +class BaseMatch { + constructor(pattern) { + this.pattern = pattern; + } + static isMultiMatch(pattern) { + return getMatch(pattern, this.multiRegex) + } + static isSingleMatch(pattern) { + return getMatch(pattern, this.singleRegex) + } + search(/*text*/) {} +} + +function getMatch(pattern, exp) { + const matches = pattern.match(exp); + return matches ? matches[1] : null +} + +// Token: 'file + +class ExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'exact' + } + static get multiRegex() { + return /^="(.*)"$/ + } + static get singleRegex() { + return /^=(.*)$/ + } + search(text) { + const isMatch = text === this.pattern; + + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + } + } +} + +// Token: !fire + +class InverseExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'inverse-exact' + } + static get multiRegex() { + return /^!"(.*)"$/ + } + static get singleRegex() { + return /^!(.*)$/ + } + search(text) { + const index = text.indexOf(this.pattern); + const isMatch = index === -1; + + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + } + } +} + +// Token: ^file + +class PrefixExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'prefix-exact' + } + static get multiRegex() { + return /^\^"(.*)"$/ + } + static get singleRegex() { + return /^\^(.*)$/ + } + search(text) { + const isMatch = text.startsWith(this.pattern); + + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + } + } +} + +// Token: !^fire + +class InversePrefixExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'inverse-prefix-exact' + } + static get multiRegex() { + return /^!\^"(.*)"$/ + } + static get singleRegex() { + return /^!\^(.*)$/ + } + search(text) { + const isMatch = !text.startsWith(this.pattern); + + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + } + } +} + +// Token: .file$ + +class SuffixExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'suffix-exact' + } + static get multiRegex() { + return /^"(.*)"\$$/ + } + static get singleRegex() { + return /^(.*)\$$/ + } + search(text) { + const isMatch = text.endsWith(this.pattern); + + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [text.length - this.pattern.length, text.length - 1] + } + } +} + +// Token: !.file$ + +class InverseSuffixExactMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'inverse-suffix-exact' + } + static get multiRegex() { + return /^!"(.*)"\$$/ + } + static get singleRegex() { + return /^!(.*)\$$/ + } + search(text) { + const isMatch = !text.endsWith(this.pattern); + return { + isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + } + } +} + +class FuzzyMatch extends BaseMatch { + constructor( + pattern, + { + location = Config.location, + threshold = Config.threshold, + distance = Config.distance, + includeMatches = Config.includeMatches, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + isCaseSensitive = Config.isCaseSensitive, + ignoreLocation = Config.ignoreLocation + } = {} + ) { + super(pattern); + this._bitapSearch = new BitapSearch(pattern, { + location, + threshold, + distance, + includeMatches, + findAllMatches, + minMatchCharLength, + isCaseSensitive, + ignoreLocation + }); + } + static get type() { + return 'fuzzy' + } + static get multiRegex() { + return /^"(.*)"$/ + } + static get singleRegex() { + return /^(.*)$/ + } + search(text) { + return this._bitapSearch.searchIn(text) + } +} + +// Token: 'file + +class IncludeMatch extends BaseMatch { + constructor(pattern) { + super(pattern); + } + static get type() { + return 'include' + } + static get multiRegex() { + return /^'"(.*)"$/ + } + static get singleRegex() { + return /^'(.*)$/ + } + search(text) { + let location = 0; + let index; + + const indices = []; + const patternLen = this.pattern.length; + + // Get all exact matches + while ((index = text.indexOf(this.pattern, location)) > -1) { + location = index + patternLen; + indices.push([index, location - 1]); + } + + const isMatch = !!indices.length; + + return { + isMatch, + score: isMatch ? 0 : 1, + indices + } + } +} + +// ❗Order is important. DO NOT CHANGE. +const searchers = [ + ExactMatch, + IncludeMatch, + PrefixExactMatch, + InversePrefixExactMatch, + InverseSuffixExactMatch, + SuffixExactMatch, + InverseExactMatch, + FuzzyMatch +]; + +const searchersLen = searchers.length; + +// Regex to split by spaces, but keep anything in quotes together +const SPACE_RE = / +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/; +const OR_TOKEN = '|'; + +// Return a 2D array representation of the query, for simpler parsing. +// Example: +// "^core go$ | rb$ | py$ xy$" => [["^core", "go$"], ["rb$"], ["py$", "xy$"]] +function parseQuery(pattern, options = {}) { + return pattern.split(OR_TOKEN).map((item) => { + let query = item + .trim() + .split(SPACE_RE) + .filter((item) => item && !!item.trim()); + + let results = []; + for (let i = 0, len = query.length; i < len; i += 1) { + const queryItem = query[i]; + + // 1. Handle multiple query match (i.e, once that are quoted, like `"hello world"`) + let found = false; + let idx = -1; + while (!found && ++idx < searchersLen) { + const searcher = searchers[idx]; + let token = searcher.isMultiMatch(queryItem); + if (token) { + results.push(new searcher(token, options)); + found = true; + } + } + + if (found) { + continue + } + + // 2. Handle single query matches (i.e, once that are *not* quoted) + idx = -1; + while (++idx < searchersLen) { + const searcher = searchers[idx]; + let token = searcher.isSingleMatch(queryItem); + if (token) { + results.push(new searcher(token, options)); + break + } + } + } + + return results + }) +} + +// These extended matchers can return an array of matches, as opposed +// to a singl match +const MultiMatchSet = new Set([FuzzyMatch.type, IncludeMatch.type]); + +/** + * Command-like searching + * ====================== + * + * Given multiple search terms delimited by spaces.e.g. `^jscript .python$ ruby !java`, + * search in a given text. + * + * Search syntax: + * + * | Token | Match type | Description | + * | ----------- | -------------------------- | -------------------------------------- | + * | `jscript` | fuzzy-match | Items that fuzzy match `jscript` | + * | `=scheme` | exact-match | Items that are `scheme` | + * | `'python` | include-match | Items that include `python` | + * | `!ruby` | inverse-exact-match | Items that do not include `ruby` | + * | `^java` | prefix-exact-match | Items that start with `java` | + * | `!^earlang` | inverse-prefix-exact-match | Items that do not start with `earlang` | + * | `.js$` | suffix-exact-match | Items that end with `.js` | + * | `!.go$` | inverse-suffix-exact-match | Items that do not end with `.go` | + * + * A single pipe character acts as an OR operator. For example, the following + * query matches entries that start with `core` and end with either`go`, `rb`, + * or`py`. + * + * ``` + * ^core go$ | rb$ | py$ + * ``` + */ +class ExtendedSearch { + constructor( + pattern, + { + isCaseSensitive = Config.isCaseSensitive, + includeMatches = Config.includeMatches, + minMatchCharLength = Config.minMatchCharLength, + ignoreLocation = Config.ignoreLocation, + findAllMatches = Config.findAllMatches, + location = Config.location, + threshold = Config.threshold, + distance = Config.distance + } = {} + ) { + this.query = null; + this.options = { + isCaseSensitive, + includeMatches, + minMatchCharLength, + findAllMatches, + ignoreLocation, + location, + threshold, + distance + }; + + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.query = parseQuery(this.pattern, this.options); + } + + static condition(_, options) { + return options.useExtendedSearch + } + + searchIn(text) { + const query = this.query; + + if (!query) { + return { + isMatch: false, + score: 1 + } + } + + const { includeMatches, isCaseSensitive } = this.options; + + text = isCaseSensitive ? text : text.toLowerCase(); + + let numMatches = 0; + let allIndices = []; + let totalScore = 0; + + // ORs + for (let i = 0, qLen = query.length; i < qLen; i += 1) { + const searchers = query[i]; + + // Reset indices + allIndices.length = 0; + numMatches = 0; + + // ANDs + for (let j = 0, pLen = searchers.length; j < pLen; j += 1) { + const searcher = searchers[j]; + const { isMatch, indices, score } = searcher.search(text); + + if (isMatch) { + numMatches += 1; + totalScore += score; + if (includeMatches) { + const type = searcher.constructor.type; + if (MultiMatchSet.has(type)) { + allIndices = [...allIndices, ...indices]; + } else { + allIndices.push(indices); + } + } + } else { + totalScore = 0; + numMatches = 0; + allIndices.length = 0; + break + } + } + + // OR condition, so if TRUE, return + if (numMatches) { + let result = { + isMatch: true, + score: totalScore / numMatches + }; + + if (includeMatches) { + result.indices = allIndices; + } + + return result + } + } + + // Nothing was matched + return { + isMatch: false, + score: 1 + } + } +} + +const registeredSearchers = []; + +function register(...args) { + registeredSearchers.push(...args); +} + +function createSearcher(pattern, options) { + for (let i = 0, len = registeredSearchers.length; i < len; i += 1) { + let searcherClass = registeredSearchers[i]; + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options) + } + } + + return new BitapSearch(pattern, options) +} + +const LogicalOperator = { + AND: '$and', + OR: '$or' +}; + +const KeyType = { + PATH: '$path', + PATTERN: '$val' +}; + +const isExpression = (query) => + !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); + +const isPath = (query) => !!query[KeyType.PATH]; + +const isLeaf = (query) => + !isArray(query) && isObject(query) && !isExpression(query); + +const convertToExplicit = (query) => ({ + [LogicalOperator.AND]: Object.keys(query).map((key) => ({ + [key]: query[key] + })) +}); + +// When `auto` is `true`, the parse function will infer and initialize and add +// the appropriate `Searcher` instance +function parse(query, options, { auto = true } = {}) { + const next = (query) => { + let keys = Object.keys(query); + + const isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)) + } + + if (isLeaf(query)) { + const key = isQueryPath ? query[KeyType.PATH] : keys[0]; + + const pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)) + } + + const obj = { + keyId: createKeyId(key), + pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj + } + + let node = { + children: [], + operator: keys[0] + }; + + keys.forEach((key) => { + const value = query[key]; + + if (isArray(value)) { + value.forEach((item) => { + node.children.push(next(item)); + }); + } + }); + + return node + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query) +} + +// Practical scoring function +function computeScore$1( + results, + { ignoreFieldNorm = Config.ignoreFieldNorm } +) { + results.forEach((result) => { + let totalScore = 1; + + result.matches.forEach(({ key, norm, score }) => { + const weight = key ? key.weight : null; + + totalScore *= Math.pow( + score === 0 && weight ? Number.EPSILON : score, + (weight || 1) * (ignoreFieldNorm ? 1 : norm) + ); + }); + + result.score = totalScore; + }); +} + +function transformMatches(result, data) { + const matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return + } + + matches.forEach((match) => { + if (!isDefined(match.indices) || !match.indices.length) { + return + } + + const { indices, value } = match; + + let obj = { + indices, + value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); +} + +function transformScore(result, data) { + data.score = result.score; +} + +function format( + results, + docs, + { + includeMatches = Config.includeMatches, + includeScore = Config.includeScore + } = {} +) { + const transformers = []; + + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + + return results.map((result) => { + const { idx } = result; + + const data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach((transformer) => { + transformer(result, data); + }); + } + + return data + }) +} + +class Fuse { + constructor(docs, options = {}, index) { + this.options = { ...Config, ...options }; + + if ( + this.options.useExtendedSearch && + !true + ) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE) + } + + this._keyStore = new KeyStore(this.options.keys); + + this.setCollection(docs, index); + } + + setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE) + } + + this._myIndex = + index || + createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + + add(doc) { + if (!isDefined(doc)) { + return + } + + this._docs.push(doc); + this._myIndex.add(doc); + } + + remove(predicate = (/* doc, idx */) => false) { + const results = []; + + for (let i = 0, len = this._docs.length; i < len; i += 1) { + const doc = this._docs[i]; + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + + results.push(doc); + } + } + + return results + } + + removeAt(idx) { + this._docs.splice(idx, 1); + this._myIndex.removeAt(idx); + } + + getIndex() { + return this._myIndex + } + + search(query, { limit = -1 } = {}) { + const { + includeMatches, + includeScore, + shouldSort, + sortFn, + ignoreFieldNorm + } = this.options; + + let results = isString(query) + ? isString(this._docs[0]) + ? this._searchStringList(query) + : this._searchObjectList(query) + : this._searchLogical(query); + + computeScore$1(results, { ignoreFieldNorm }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches, + includeScore + }) + } + + _searchStringList(query) { + const searcher = createSearcher(query, this.options); + const { records } = this._myIndex; + const results = []; + + // Iterate over every string in the index + records.forEach(({ v: text, i: idx, n: norm }) => { + if (!isDefined(text)) { + return + } + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + results.push({ + item: text, + idx, + matches: [{ score, value: text, norm, indices }] + }); + } + }); + + return results + } + + _searchLogical(query) { + + const expression = parse(query, this.options); + + const evaluate = (node, item, idx) => { + if (!node.children) { + const { keyId, searcher } = node; + + const matches = this._findMatches({ + key: this._keyStore.get(keyId), + value: this._myIndex.getValueForItemAtKeyId(item, keyId), + searcher + }); + + if (matches && matches.length) { + return [ + { + idx, + item, + matches + } + ] + } + + return [] + } + + /*eslint indent: [2, 2, {"SwitchCase": 1}]*/ + switch (node.operator) { + case LogicalOperator.AND: { + const res = []; + for (let i = 0, len = node.children.length; i < len; i += 1) { + const child = node.children[i]; + const result = evaluate(child, item, idx); + if (result.length) { + res.push(...result); + } else { + return [] + } + } + return res + } + case LogicalOperator.OR: { + const res = []; + for (let i = 0, len = node.children.length; i < len; i += 1) { + const child = node.children[i]; + const result = evaluate(child, item, idx); + if (result.length) { + res.push(...result); + break + } + } + return res + } + } + }; + + const records = this._myIndex.records; + const resultMap = {}; + const results = []; + + records.forEach(({ $: item, i: idx }) => { + if (isDefined(item)) { + let expResults = evaluate(expression, item, idx); + + if (expResults.length) { + // Dedupe when adding + if (!resultMap[idx]) { + resultMap[idx] = { idx, item, matches: [] }; + results.push(resultMap[idx]); + } + expResults.forEach(({ matches }) => { + resultMap[idx].matches.push(...matches); + }); + } + } + }); + + return results + } + + _searchObjectList(query) { + const searcher = createSearcher(query, this.options); + const { keys, records } = this._myIndex; + const results = []; + + // List is Array + records.forEach(({ $: item, i: idx }) => { + if (!isDefined(item)) { + return + } + + let matches = []; + + // Iterate over every key (i.e, path), and fetch the value at that key + keys.forEach((key, keyIndex) => { + matches.push( + ...this._findMatches({ + key, + value: item[keyIndex], + searcher + }) + ); + }); + + if (matches.length) { + results.push({ + idx, + item, + matches + }); + } + }); + + return results + } + _findMatches({ key, value, searcher }) { + if (!isDefined(value)) { + return [] + } + + let matches = []; + + if (isArray(value)) { + value.forEach(({ v: text, i: idx, n: norm }) => { + if (!isDefined(text)) { + return + } + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + matches.push({ + score, + key, + value: text, + idx, + norm, + indices + }); + } + }); + } else { + const { v: text, n: norm } = value; + + const { isMatch, score, indices } = searcher.searchIn(text); + + if (isMatch) { + matches.push({ score, key, value: text, norm, indices }); + } + } + + return matches + } +} + +Fuse.version = '6.4.6'; +Fuse.createIndex = createIndex; +Fuse.parseIndex = parseIndex; +Fuse.config = Config; + +{ + Fuse.parseQuery = parse; +} + +{ + register(ExtendedSearch); +} + +export default Fuse; diff --git a/node_modules/fuse.js/dist/fuse.esm.min.js b/node_modules/fuse.js/dist/fuse.esm.min.js new file mode 100644 index 0000000..79b5c13 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.esm.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +function t(t){return Array.isArray?Array.isArray(t):"[object Array]"===o(t)}function e(t){return"string"==typeof t}function n(t){return"number"==typeof t}function s(t){return!0===t||!1===t||function(t){return r(t)&&null!==t}(t)&&"[object Boolean]"==o(t)}function r(t){return"object"==typeof t}function i(t){return null!=t}function c(t){return!t.trim().length}function o(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":Object.prototype.toString.call(t)}const h=Object.prototype.hasOwnProperty;class a{constructor(t){this._keys=[],this._keyMap={};let e=0;t.forEach(t=>{let n=l(t);e+=n.weight,this._keys.push(n),this._keyMap[n.id]=n,e+=n.weight}),this._keys.forEach(t=>{t.weight/=e})}get(t){return this._keyMap[t]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function l(n){let s=null,r=null,i=null,c=1;if(e(n)||t(n))i=n,s=u(n),r=d(n);else{if(!h.call(n,"name"))throw new Error((t=>`Missing ${t} property in key`)("name"));const t=n.name;if(i=t,h.call(n,"weight")&&(c=n.weight,c<=0))throw new Error((t=>`Property 'weight' in key '${t}' must be a positive integer`)(t));s=u(t),r=d(t)}return{path:s,id:r,weight:c,src:i}}function u(e){return t(e)?e:e.split(".")}function d(e){return t(e)?e.join("."):e}var g={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(t,e)=>t.score===e.score?t.idx{if(i(r))if(c[l]){const u=r[c[l]];if(!i(u))return;if(l===c.length-1&&(e(u)||n(u)||s(u)))o.push(function(t){return null==t?"":function(t){if("string"==typeof t)return t;let e=t+"";return"0"==e&&1/t==-1/0?"-0":e}(t)}(u));else if(t(u)){h=!0;for(let t=0,e=u.length;t{this._keysMap[t.id]=e})}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,e(this.docs[0])?this.docs.forEach((t,e)=>{this._addString(t,e)}):this.docs.forEach((t,e)=>{this._addObject(t,e)}),this.norm.clear())}add(t){const n=this.size();e(t)?this._addString(t,n):this._addObject(t,n)}removeAt(t){this.records.splice(t,1);for(let e=t,n=this.size();e{let h=this.getFn(n,s.path);if(i(h))if(t(h)){let n=[];const s=[{nestedArrIndex:-1,value:h}];for(;s.length;){const{nestedArrIndex:r,value:o}=s.pop();if(i(o))if(e(o)&&!c(o)){let t={v:o,i:r,n:this.norm.get(o)};n.push(t)}else t(o)&&o.forEach((t,e)=>{s.push({nestedArrIndex:e,value:t})})}r.$[o]=n}else if(!c(h)){let t={v:h,n:this.norm.get(h)};r.$[o]=t}}),this.records.push(r)}toJSON(){return{keys:this.keys,records:this.records}}}function M(t,e,{getFn:n=g.getFn}={}){const s=new p({getFn:n});return s.setKeys(t.map(l)),s.setSources(e),s.create(),s}function m(t,{errors:e=0,currentLocation:n=0,expectedLocation:s=0,distance:r=g.distance,ignoreLocation:i=g.ignoreLocation}={}){const c=e/t.length;if(i)return c;const o=Math.abs(s-n);return r?c+o/r:o?1:c}function x(t,e,n,{location:s=g.location,distance:r=g.distance,threshold:i=g.threshold,findAllMatches:c=g.findAllMatches,minMatchCharLength:o=g.minMatchCharLength,includeMatches:h=g.includeMatches,ignoreLocation:a=g.ignoreLocation}={}){if(e.length>32)throw new Error(`Pattern length exceeds max of ${32}.`);const l=e.length,u=t.length,d=Math.max(0,Math.min(s,u));let f=i,p=d;const M=o>1||h,x=M?Array(u):[];let y;for(;(y=t.indexOf(e,p))>-1;){let t=m(e,{currentLocation:y,expectedLocation:d,distance:r,ignoreLocation:a});if(f=Math.min(t,f),p=y+l,M){let t=0;for(;t=h;i-=1){let c=i-1,o=n[t.charAt(c)];if(M&&(x[c]=+!!o),y[i]=(y[i+1]<<1|1)&o,s&&(y[i]|=(L[i+1]|L[i])<<1|1|L[i+1]),y[i]&v&&(k=m(e,{errors:s,currentLocation:c,expectedLocation:d,distance:r,ignoreLocation:a}),k<=f)){if(f=k,p=c,p<=d)break;h=Math.max(1,2*d-p)}}if(m(e,{errors:s+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:a})>f)break;L=y}const S={isMatch:p>=0,score:Math.max(.001,k)};if(M){const t=function(t=[],e=g.minMatchCharLength){let n=[],s=-1,r=-1,i=0;for(let c=t.length;i=e&&n.push([s,r]),s=-1)}return t[i-1]&&i-s>=e&&n.push([s,i-1]),n}(x,o);t.length?h&&(S.indices=t):S.isMatch=!1}return S}function y(t){let e={};for(let n=0,s=t.length;n{this.chunks.push({pattern:t,alphabet:y(t),startIndex:e})},l=this.pattern.length;if(l>32){let t=0;const e=l%32,n=l-e;for(;t{const{isMatch:f,score:p,indices:M}=x(t,e,d,{location:s+g,distance:r,threshold:i,findAllMatches:c,minMatchCharLength:o,includeMatches:n,ignoreLocation:h});f&&(u=!0),l+=p,f&&M&&(a=[...a,...M])});let d={isMatch:u,score:u?l/this.chunks.length:1};return u&&n&&(d.indices=a),d}}class k{constructor(t){this.pattern=t}static isMultiMatch(t){return _(t,this.multiRegex)}static isSingleMatch(t){return _(t,this.singleRegex)}search(){}}function _(t,e){const n=t.match(e);return n?n[1]:null}class v extends k{constructor(t,{location:e=g.location,threshold:n=g.threshold,distance:s=g.distance,includeMatches:r=g.includeMatches,findAllMatches:i=g.findAllMatches,minMatchCharLength:c=g.minMatchCharLength,isCaseSensitive:o=g.isCaseSensitive,ignoreLocation:h=g.ignoreLocation}={}){super(t),this._bitapSearch=new L(t,{location:e,threshold:n,distance:s,includeMatches:r,findAllMatches:i,minMatchCharLength:c,isCaseSensitive:o,ignoreLocation:h})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(t){return this._bitapSearch.searchIn(t)}}class S extends k{constructor(t){super(t)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(t){let e,n=0;const s=[],r=this.pattern.length;for(;(e=t.indexOf(this.pattern,n))>-1;)n=e+r,s.push([e,n-1]);const i=!!s.length;return{isMatch:i,score:i?0:1,indices:s}}}const w=[class extends k{constructor(t){super(t)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(t){const e=t===this.pattern;return{isMatch:e,score:e?0:1,indices:[0,this.pattern.length-1]}}},S,class extends k{constructor(t){super(t)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(t){const e=t.startsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,this.pattern.length-1]}}},class extends k{constructor(t){super(t)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(t){const e=!t.startsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},class extends k{constructor(t){super(t)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(t){const e=!t.endsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},class extends k{constructor(t){super(t)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(t){const e=t.endsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[t.length-this.pattern.length,t.length-1]}}},class extends k{constructor(t){super(t)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(t){const e=-1===t.indexOf(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},v],C=w.length,I=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;const $=new Set([v.type,S.type]);class A{constructor(t,{isCaseSensitive:e=g.isCaseSensitive,includeMatches:n=g.includeMatches,minMatchCharLength:s=g.minMatchCharLength,ignoreLocation:r=g.ignoreLocation,findAllMatches:i=g.findAllMatches,location:c=g.location,threshold:o=g.threshold,distance:h=g.distance}={}){this.query=null,this.options={isCaseSensitive:e,includeMatches:n,minMatchCharLength:s,findAllMatches:i,ignoreLocation:r,location:c,threshold:o,distance:h},this.pattern=e?t:t.toLowerCase(),this.query=function(t,e={}){return t.split("|").map(t=>{let n=t.trim().split(I).filter(t=>t&&!!t.trim()),s=[];for(let t=0,r=n.length;t!(!t[F]&&!t[R]),z=t=>({[F]:Object.keys(t).map(e=>({[e]:t[e]}))});function K(n,s,{auto:i=!0}={}){const c=n=>{let o=Object.keys(n);const h=(t=>!!t[O])(n);if(!h&&o.length>1&&!N(n))return c(z(n));if((e=>!t(e)&&r(e)&&!N(e))(n)){const t=h?n[O]:o[0],r=h?n[j]:n[t];if(!e(r))throw new Error((t=>"Invalid value for key "+t)(t));const c={keyId:d(t),pattern:r};return i&&(c.searcher=E(r,s)),c}let a={children:[],operator:o[0]};return o.forEach(e=>{const s=n[e];t(s)&&s.forEach(t=>{a.children.push(c(t))})}),a};return N(n)||(n=z(n)),c(n)}function q(t,e){const n=t.matches;e.matches=[],i(n)&&n.forEach(t=>{if(!i(t.indices)||!t.indices.length)return;const{indices:n,value:s}=t;let r={indices:n,value:s};t.key&&(r.key=t.key.src),t.idx>-1&&(r.refIndex=t.idx),e.matches.push(r)})}function P(t,e){e.score=t.score}class W{constructor(t,e={},n){this.options={...g,...e},this.options.useExtendedSearch,this._keyStore=new a(this.options.keys),this.setCollection(t,n)}setCollection(t,e){if(this._docs=t,e&&!(e instanceof p))throw new Error("Incorrect 'index' type");this._myIndex=e||M(this.options.keys,this._docs,{getFn:this.options.getFn})}add(t){i(t)&&(this._docs.push(t),this._myIndex.add(t))}remove(t=(()=>!1)){const e=[];for(let n=0,s=this._docs.length;n{let n=1;t.matches.forEach(({key:t,norm:s,score:r})=>{const i=t?t.weight:null;n*=Math.pow(0===r&&i?Number.EPSILON:r,(i||1)*(e?1:s))}),t.score=n})}(a,{ignoreFieldNorm:h}),c&&a.sort(o),n(s)&&s>-1&&(a=a.slice(0,s)),function(t,e,{includeMatches:n=g.includeMatches,includeScore:s=g.includeScore}={}){const r=[];return n&&r.push(q),s&&r.push(P),t.map(t=>{const{idx:n}=t,s={item:e[n],refIndex:n};return r.length&&r.forEach(e=>{e(t,s)}),s})}(a,this._docs,{includeMatches:r,includeScore:i})}_searchStringList(t){const e=E(t,this.options),{records:n}=this._myIndex,s=[];return n.forEach(({v:t,i:n,n:r})=>{if(!i(t))return;const{isMatch:c,score:o,indices:h}=e.searchIn(t);c&&s.push({item:t,idx:n,matches:[{score:o,value:t,norm:r,indices:h}]})}),s}_searchLogical(t){const e=K(t,this.options),n=(t,e,s)=>{if(!t.children){const{keyId:n,searcher:r}=t,i=this._findMatches({key:this._keyStore.get(n),value:this._myIndex.getValueForItemAtKeyId(e,n),searcher:r});return i&&i.length?[{idx:s,item:e,matches:i}]:[]}switch(t.operator){case F:{const r=[];for(let i=0,c=t.children.length;i{if(i(t)){let i=n(e,t,s);i.length&&(r[s]||(r[s]={idx:s,item:t,matches:[]},c.push(r[s])),i.forEach(({matches:t})=>{r[s].matches.push(...t)}))}}),c}_searchObjectList(t){const e=E(t,this.options),{keys:n,records:s}=this._myIndex,r=[];return s.forEach(({$:t,i:s})=>{if(!i(t))return;let c=[];n.forEach((n,s)=>{c.push(...this._findMatches({key:n,value:t[s],searcher:e}))}),c.length&&r.push({idx:s,item:t,matches:c})}),r}_findMatches({key:e,value:n,searcher:s}){if(!i(n))return[];let r=[];if(t(n))n.forEach(({v:t,i:n,n:c})=>{if(!i(t))return;const{isMatch:o,score:h,indices:a}=s.searchIn(t);o&&r.push({score:h,key:e,value:t,idx:n,norm:c,indices:a})});else{const{v:t,n:i}=n,{isMatch:c,score:o,indices:h}=s.searchIn(t);c&&r.push({score:o,key:e,value:t,norm:i,indices:h})}return r}}W.version="6.4.6",W.createIndex=M,W.parseIndex=function(t,{getFn:e=g.getFn}={}){const{keys:n,records:s}=t,r=new p({getFn:e});return r.setKeys(n),r.setIndexRecords(s),r},W.config=g,function(...t){b.push(...t)}(A);export default W; \ No newline at end of file diff --git a/node_modules/fuse.js/dist/fuse.js b/node_modules/fuse.js/dist/fuse.js new file mode 100644 index 0000000..6be0592 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.js @@ -0,0 +1,2255 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Fuse = factory()); +}(this, (function () { 'use strict'; + + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); + } + + function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); + } + + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); + } + + function _isNativeReflectConstruct() { + if (typeof Reflect === "undefined" || !Reflect.construct) return false; + if (Reflect.construct.sham) return false; + if (typeof Proxy === "function") return true; + + try { + Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); + return true; + } catch (e) { + return false; + } + } + + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; + } + + function _possibleConstructorReturn(self, call) { + if (call && (typeof call === "object" || typeof call === "function")) { + return call; + } + + return _assertThisInitialized(self); + } + + function _createSuper(Derived) { + var hasNativeReflectConstruct = _isNativeReflectConstruct(); + + return function _createSuperInternal() { + var Super = _getPrototypeOf(Derived), + result; + + if (hasNativeReflectConstruct) { + var NewTarget = _getPrototypeOf(this).constructor; + + result = Reflect.construct(Super, arguments, NewTarget); + } else { + result = Super.apply(this, arguments); + } + + return _possibleConstructorReturn(this, result); + }; + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); + } + + function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); + } // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js + + var INFINITY = 1 / 0; + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + + var result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; + } + function toString(value) { + return value == null ? '' : baseToString(value); + } + function isString(value) { + return typeof value === 'string'; + } + function isNumber(value) { + return typeof value === 'number'; + } // Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js + + function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; + } + function isObject(value) { + return _typeof(value) === 'object'; + } // Checks if `value` is object-like. + + function isObjectLike(value) { + return isObject(value) && value !== null; + } + function isDefined(value) { + return value !== undefined && value !== null; + } + function isBlank(value) { + return !value.trim().length; + } // Gets the `toStringTag` of `value`. + // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js + + function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); + } + + var EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; + var INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; + var LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = function LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key) { + return "Invalid value for key ".concat(key); + }; + var PATTERN_LENGTH_TOO_LARGE = function PATTERN_LENGTH_TOO_LARGE(max) { + return "Pattern length exceeds max of ".concat(max, "."); + }; + var MISSING_KEY_PROPERTY = function MISSING_KEY_PROPERTY(name) { + return "Missing ".concat(name, " property in key"); + }; + var INVALID_KEY_WEIGHT_VALUE = function INVALID_KEY_WEIGHT_VALUE(key) { + return "Property 'weight' in key '".concat(key, "' must be a positive integer"); + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var KeyStore = /*#__PURE__*/function () { + function KeyStore(keys) { + var _this = this; + + _classCallCheck(this, KeyStore); + + this._keys = []; + this._keyMap = {}; + var totalWeight = 0; + keys.forEach(function (key) { + var obj = createKey(key); + totalWeight += obj.weight; + + _this._keys.push(obj); + + _this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); // Normalize weights so that their sum is equal to 1 + + this._keys.forEach(function (key) { + key.weight /= totalWeight; + }); + } + + _createClass(KeyStore, [{ + key: "get", + value: function get(keyId) { + return this._keyMap[keyId]; + } + }, { + key: "keys", + value: function keys() { + return this._keys; + } + }, { + key: "toJSON", + value: function toJSON() { + return JSON.stringify(this._keys); + } + }]); + + return KeyStore; + }(); + function createKey(key) { + var path = null; + var id = null; + var src = null; + var weight = 1; + + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + + var name = key.name; + src = name; + + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + + path = createKeyPath(name); + id = createKeyId(name); + } + + return { + path: path, + id: id, + weight: weight, + src: src + }; + } + function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); + } + function createKeyId(key) { + return isArray(key) ? key.join('.') : key; + } + + function get(obj, path) { + var list = []; + var arr = false; + + var deepGet = function deepGet(obj, path, index) { + if (!isDefined(obj)) { + return; + } + + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + var key = path[index]; + var value = obj[key]; + + if (!isDefined(value)) { + return; + } // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + + + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; // Search each item in the array. + + for (var i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; // Backwards compatibility (since path used to be a string) + + + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; + } + + var MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 + }; + var BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: function sortFn(a, b) { + return a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1; + } + }; + var FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 + }; + var AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false + }; + var Config = _objectSpread2({}, BasicOptions, {}, MatchOptions, {}, FuzzyOptions, {}, AdvancedOptions); + + var SPACE = /[^ ]+/g; // Field-length norm: the shorter the field, the higher the weight. + // Set to 3 decimals to reduce index size. + + function norm() { + var mantissa = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3; + var cache = new Map(); + var m = Math.pow(10, mantissa); + return { + get: function get(value) { + var numTokens = value.match(SPACE).length; + + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + var norm = 1 / Math.sqrt(numTokens); // In place of `toFixed(mantissa)`, for faster computation + + var n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear: function clear() { + cache.clear(); + } + }; + } + + var FuseIndex = /*#__PURE__*/function () { + function FuseIndex() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$getFn = _ref.getFn, + getFn = _ref$getFn === void 0 ? Config.getFn : _ref$getFn; + + _classCallCheck(this, FuseIndex); + + this.norm = norm(3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + + _createClass(FuseIndex, [{ + key: "setSources", + value: function setSources() { + var docs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.docs = docs; + } + }, { + key: "setIndexRecords", + value: function setIndexRecords() { + var records = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.records = records; + } + }, { + key: "setKeys", + value: function setKeys() { + var _this = this; + + var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + this.keys = keys; + this._keysMap = {}; + keys.forEach(function (key, idx) { + _this._keysMap[key.id] = idx; + }); + } + }, { + key: "create", + value: function create() { + var _this2 = this; + + if (this.isCreated || !this.docs.length) { + return; + } + + this.isCreated = true; // List is Array + + if (isString(this.docs[0])) { + this.docs.forEach(function (doc, docIndex) { + _this2._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach(function (doc, docIndex) { + _this2._addObject(doc, docIndex); + }); + } + + this.norm.clear(); + } // Adds a doc to the end of the index + + }, { + key: "add", + value: function add(doc) { + var idx = this.size(); + + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } // Removes the doc at the specified index of the index + + }, { + key: "removeAt", + value: function removeAt(idx) { + this.records.splice(idx, 1); // Change ref index of every subsquent doc + + for (var i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + }, { + key: "getValueForItemAtKeyId", + value: function getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + }, { + key: "size", + value: function size() { + return this.records.length; + } + }, { + key: "_addString", + value: function _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + + var record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + }, { + key: "_addObject", + value: function _addObject(doc, docIndex) { + var _this3 = this; + + var record = { + i: docIndex, + $: {} + }; // Iterate over every key (i.e, path), and fetch the value at that key + + this.keys.forEach(function (key, keyIndex) { + // console.log(key) + var value = _this3.getFn(doc, key.path); + + if (!isDefined(value)) { + return; + } + + if (isArray(value)) { + (function () { + var subRecords = []; + var stack = [{ + nestedArrIndex: -1, + value: value + }]; + + while (stack.length) { + var _stack$pop = stack.pop(), + nestedArrIndex = _stack$pop.nestedArrIndex, + _value = _stack$pop.value; + + if (!isDefined(_value)) { + continue; + } + + if (isString(_value) && !isBlank(_value)) { + var subRecord = { + v: _value, + i: nestedArrIndex, + n: _this3.norm.get(_value) + }; + subRecords.push(subRecord); + } else if (isArray(_value)) { + _value.forEach(function (item, k) { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } + } + + record.$[keyIndex] = subRecords; + })(); + } else if (!isBlank(value)) { + var subRecord = { + v: value, + n: _this3.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + }, { + key: "toJSON", + value: function toJSON() { + return { + keys: this.keys, + records: this.records + }; + } + }]); + + return FuseIndex; + }(); + function createIndex(keys, docs) { + var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref2$getFn = _ref2.getFn, + getFn = _ref2$getFn === void 0 ? Config.getFn : _ref2$getFn; + + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; + } + function parseIndex(data) { + var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref3$getFn = _ref3.getFn, + getFn = _ref3$getFn === void 0 ? Config.getFn : _ref3$getFn; + + var keys = data.keys, + records = data.records; + var myIndex = new FuseIndex({ + getFn: getFn + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; + } + + function computeScore(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$errors = _ref.errors, + errors = _ref$errors === void 0 ? 0 : _ref$errors, + _ref$currentLocation = _ref.currentLocation, + currentLocation = _ref$currentLocation === void 0 ? 0 : _ref$currentLocation, + _ref$expectedLocation = _ref.expectedLocation, + expectedLocation = _ref$expectedLocation === void 0 ? 0 : _ref$expectedLocation, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + var accuracy = errors / pattern.length; + + if (ignoreLocation) { + return accuracy; + } + + var proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + + return accuracy + proximity / distance; + } + + function convertMaskToIndices() { + var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Config.minMatchCharLength; + var indices = []; + var start = -1; + var end = -1; + var i = 0; + + for (var len = matchmask.length; i < len; i += 1) { + var match = matchmask[i]; + + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + + start = -1; + } + } // (i-1 - start) + 1 => i - start + + + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + + return indices; + } + + // Machine word size + var MAX_BITS = 32; + + function search(text, pattern, patternAlphabet) { + var _ref = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + + var patternLen = pattern.length; // Set starting location at beginning text and initialize the alphabet. + + var textLen = text.length; // Handle the case when location > text.length + + var expectedLocation = Math.max(0, Math.min(location, textLen)); // Highest score beyond which we give up. + + var currentThreshold = threshold; // Is there a nearby exact match? (speedup) + + var bestLocation = expectedLocation; // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + + var computeMatches = minMatchCharLength > 1 || includeMatches; // A mask of the matches, used for building the indices + + var matchMask = computeMatches ? Array(textLen) : []; + var index; // Get all exact matches, here for speed up + + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + var score = computeScore(pattern, { + currentLocation: index, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + + if (computeMatches) { + var i = 0; + + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } // Reset the best location + + + bestLocation = -1; + var lastBitArr = []; + var finalScore = 1; + var binMax = patternLen + textLen; + var mask = 1 << patternLen - 1; + + for (var _i = 0; _i < patternLen; _i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + var binMin = 0; + var binMid = binMax; + + while (binMin < binMid) { + var _score2 = computeScore(pattern, { + errors: _i, + currentLocation: expectedLocation + binMid, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score2 <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } // Use the result from this iteration as the maximum for the next. + + + binMax = binMid; + var start = Math.max(1, expectedLocation - binMid + 1); + var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; // Initialize the bit array + + var bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << _i) - 1; + + for (var j = finish; j >= start; j -= 1) { + var currentLocation = j - 1; + var charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } // First pass: exact match + + + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; // Subsequent passes: fuzzy match + + if (_i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = computeScore(pattern, { + errors: _i, + currentLocation: currentLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); // This match will almost certainly be better than any existing match. + // But check anyway. + + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; // Already passed `loc`, downhill from here on in. + + if (bestLocation <= expectedLocation) { + break; + } // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + + + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } // No hope for a (better) match at greater error levels. + + + var _score = computeScore(pattern, { + errors: _i + 1, + currentLocation: expectedLocation, + expectedLocation: expectedLocation, + distance: distance, + ignoreLocation: ignoreLocation + }); + + if (_score > currentThreshold) { + break; + } + + lastBitArr = bitArr; + } + + var result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + + if (computeMatches) { + var indices = convertMaskToIndices(matchMask, minMatchCharLength); + + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + + return result; + } + + function createPatternAlphabet(pattern) { + var mask = {}; + + for (var i = 0, len = pattern.length; i < len; i += 1) { + var char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + + return mask; + } + + var BitapSearch = /*#__PURE__*/function () { + function BitapSearch(pattern) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, BitapSearch); + + this.options = { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + + if (!this.pattern.length) { + return; + } + + var addChunk = function addChunk(pattern, startIndex) { + _this.chunks.push({ + pattern: pattern, + alphabet: createPatternAlphabet(pattern), + startIndex: startIndex + }); + }; + + var len = this.pattern.length; + + if (len > MAX_BITS) { + var i = 0; + var remainder = len % MAX_BITS; + var end = len - remainder; + + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + + if (remainder) { + var startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + + _createClass(BitapSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var _this$options = this.options, + isCaseSensitive = _this$options.isCaseSensitive, + includeMatches = _this$options.includeMatches; + + if (!isCaseSensitive) { + text = text.toLowerCase(); + } // Exact match + + + if (this.pattern === text) { + var _result = { + isMatch: true, + score: 0 + }; + + if (includeMatches) { + _result.indices = [[0, text.length - 1]]; + } + + return _result; + } // Otherwise, use Bitap algorithm + + + var _this$options2 = this.options, + location = _this$options2.location, + distance = _this$options2.distance, + threshold = _this$options2.threshold, + findAllMatches = _this$options2.findAllMatches, + minMatchCharLength = _this$options2.minMatchCharLength, + ignoreLocation = _this$options2.ignoreLocation; + var allIndices = []; + var totalScore = 0; + var hasMatches = false; + this.chunks.forEach(function (_ref2) { + var pattern = _ref2.pattern, + alphabet = _ref2.alphabet, + startIndex = _ref2.startIndex; + + var _search = search(text, pattern, alphabet, { + location: location + startIndex, + distance: distance, + threshold: threshold, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + includeMatches: includeMatches, + ignoreLocation: ignoreLocation + }), + isMatch = _search.isMatch, + score = _search.score, + indices = _search.indices; + + if (isMatch) { + hasMatches = true; + } + + totalScore += score; + + if (isMatch && indices) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } + }); + var result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + + return result; + } + }]); + + return BitapSearch; + }(); + + var BaseMatch = /*#__PURE__*/function () { + function BaseMatch(pattern) { + _classCallCheck(this, BaseMatch); + + this.pattern = pattern; + } + + _createClass(BaseMatch, [{ + key: "search", + value: function search() + /*text*/ + {} + }], [{ + key: "isMultiMatch", + value: function isMultiMatch(pattern) { + return getMatch(pattern, this.multiRegex); + } + }, { + key: "isSingleMatch", + value: function isSingleMatch(pattern) { + return getMatch(pattern, this.singleRegex); + } + }]); + + return BaseMatch; + }(); + + function getMatch(pattern, exp) { + var matches = pattern.match(exp); + return matches ? matches[1] : null; + } + + var ExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(ExactMatch, _BaseMatch); + + var _super = _createSuper(ExactMatch); + + function ExactMatch(pattern) { + _classCallCheck(this, ExactMatch); + + return _super.call(this, pattern); + } + + _createClass(ExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text === this.pattern; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^="(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^=(.*)$/; + } + }]); + + return ExactMatch; + }(BaseMatch); + + var InverseExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InverseExactMatch, _BaseMatch); + + var _super = _createSuper(InverseExactMatch); + + function InverseExactMatch(pattern) { + _classCallCheck(this, InverseExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InverseExactMatch, [{ + key: "search", + value: function search(text) { + var index = text.indexOf(this.pattern); + var isMatch = index === -1; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!(.*)$/; + } + }]); + + return InverseExactMatch; + }(BaseMatch); + + var PrefixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(PrefixExactMatch, _BaseMatch); + + var _super = _createSuper(PrefixExactMatch); + + function PrefixExactMatch(pattern) { + _classCallCheck(this, PrefixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(PrefixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text.startsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, this.pattern.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'prefix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^\^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^\^(.*)$/; + } + }]); + + return PrefixExactMatch; + }(BaseMatch); + + var InversePrefixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InversePrefixExactMatch, _BaseMatch); + + var _super = _createSuper(InversePrefixExactMatch); + + function InversePrefixExactMatch(pattern) { + _classCallCheck(this, InversePrefixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InversePrefixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = !text.startsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-prefix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!\^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!\^(.*)$/; + } + }]); + + return InversePrefixExactMatch; + }(BaseMatch); + + var SuffixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(SuffixExactMatch, _BaseMatch); + + var _super = _createSuper(SuffixExactMatch); + + function SuffixExactMatch(pattern) { + _classCallCheck(this, SuffixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(SuffixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = text.endsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [text.length - this.pattern.length, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'suffix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^"(.*)"\$$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^(.*)\$$/; + } + }]); + + return SuffixExactMatch; + }(BaseMatch); + + var InverseSuffixExactMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(InverseSuffixExactMatch, _BaseMatch); + + var _super = _createSuper(InverseSuffixExactMatch); + + function InverseSuffixExactMatch(pattern) { + _classCallCheck(this, InverseSuffixExactMatch); + + return _super.call(this, pattern); + } + + _createClass(InverseSuffixExactMatch, [{ + key: "search", + value: function search(text) { + var isMatch = !text.endsWith(this.pattern); + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: [0, text.length - 1] + }; + } + }], [{ + key: "type", + get: function get() { + return 'inverse-suffix-exact'; + } + }, { + key: "multiRegex", + get: function get() { + return /^!"(.*)"\$$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^!(.*)\$$/; + } + }]); + + return InverseSuffixExactMatch; + }(BaseMatch); + + var FuzzyMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(FuzzyMatch, _BaseMatch); + + var _super = _createSuper(FuzzyMatch); + + function FuzzyMatch(pattern) { + var _this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation; + + _classCallCheck(this, FuzzyMatch); + + _this = _super.call(this, pattern); + _this._bitapSearch = new BitapSearch(pattern, { + location: location, + threshold: threshold, + distance: distance, + includeMatches: includeMatches, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + isCaseSensitive: isCaseSensitive, + ignoreLocation: ignoreLocation + }); + return _this; + } + + _createClass(FuzzyMatch, [{ + key: "search", + value: function search(text) { + return this._bitapSearch.searchIn(text); + } + }], [{ + key: "type", + get: function get() { + return 'fuzzy'; + } + }, { + key: "multiRegex", + get: function get() { + return /^"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^(.*)$/; + } + }]); + + return FuzzyMatch; + }(BaseMatch); + + var IncludeMatch = /*#__PURE__*/function (_BaseMatch) { + _inherits(IncludeMatch, _BaseMatch); + + var _super = _createSuper(IncludeMatch); + + function IncludeMatch(pattern) { + _classCallCheck(this, IncludeMatch); + + return _super.call(this, pattern); + } + + _createClass(IncludeMatch, [{ + key: "search", + value: function search(text) { + var location = 0; + var index; + var indices = []; + var patternLen = this.pattern.length; // Get all exact matches + + while ((index = text.indexOf(this.pattern, location)) > -1) { + location = index + patternLen; + indices.push([index, location - 1]); + } + + var isMatch = !!indices.length; + return { + isMatch: isMatch, + score: isMatch ? 0 : 1, + indices: indices + }; + } + }], [{ + key: "type", + get: function get() { + return 'include'; + } + }, { + key: "multiRegex", + get: function get() { + return /^'"(.*)"$/; + } + }, { + key: "singleRegex", + get: function get() { + return /^'(.*)$/; + } + }]); + + return IncludeMatch; + }(BaseMatch); + + var searchers = [ExactMatch, IncludeMatch, PrefixExactMatch, InversePrefixExactMatch, InverseSuffixExactMatch, SuffixExactMatch, InverseExactMatch, FuzzyMatch]; + var searchersLen = searchers.length; // Regex to split by spaces, but keep anything in quotes together + + var SPACE_RE = / +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/; + var OR_TOKEN = '|'; // Return a 2D array representation of the query, for simpler parsing. + // Example: + // "^core go$ | rb$ | py$ xy$" => [["^core", "go$"], ["rb$"], ["py$", "xy$"]] + + function parseQuery(pattern) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + return pattern.split(OR_TOKEN).map(function (item) { + var query = item.trim().split(SPACE_RE).filter(function (item) { + return item && !!item.trim(); + }); + var results = []; + + for (var i = 0, len = query.length; i < len; i += 1) { + var queryItem = query[i]; // 1. Handle multiple query match (i.e, once that are quoted, like `"hello world"`) + + var found = false; + var idx = -1; + + while (!found && ++idx < searchersLen) { + var searcher = searchers[idx]; + var token = searcher.isMultiMatch(queryItem); + + if (token) { + results.push(new searcher(token, options)); + found = true; + } + } + + if (found) { + continue; + } // 2. Handle single query matches (i.e, once that are *not* quoted) + + + idx = -1; + + while (++idx < searchersLen) { + var _searcher = searchers[idx]; + + var _token = _searcher.isSingleMatch(queryItem); + + if (_token) { + results.push(new _searcher(_token, options)); + break; + } + } + } + + return results; + }); + } + + // to a singl match + + var MultiMatchSet = new Set([FuzzyMatch.type, IncludeMatch.type]); + /** + * Command-like searching + * ====================== + * + * Given multiple search terms delimited by spaces.e.g. `^jscript .python$ ruby !java`, + * search in a given text. + * + * Search syntax: + * + * | Token | Match type | Description | + * | ----------- | -------------------------- | -------------------------------------- | + * | `jscript` | fuzzy-match | Items that fuzzy match `jscript` | + * | `=scheme` | exact-match | Items that are `scheme` | + * | `'python` | include-match | Items that include `python` | + * | `!ruby` | inverse-exact-match | Items that do not include `ruby` | + * | `^java` | prefix-exact-match | Items that start with `java` | + * | `!^earlang` | inverse-prefix-exact-match | Items that do not start with `earlang` | + * | `.js$` | suffix-exact-match | Items that end with `.js` | + * | `!.go$` | inverse-suffix-exact-match | Items that do not end with `.go` | + * + * A single pipe character acts as an OR operator. For example, the following + * query matches entries that start with `core` and end with either`go`, `rb`, + * or`py`. + * + * ``` + * ^core go$ | rb$ | py$ + * ``` + */ + + var ExtendedSearch = /*#__PURE__*/function () { + function ExtendedSearch(pattern) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? Config.isCaseSensitive : _ref$isCaseSensitive, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? Config.minMatchCharLength : _ref$minMatchCharLeng, + _ref$ignoreLocation = _ref.ignoreLocation, + ignoreLocation = _ref$ignoreLocation === void 0 ? Config.ignoreLocation : _ref$ignoreLocation, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? Config.findAllMatches : _ref$findAllMatches, + _ref$location = _ref.location, + location = _ref$location === void 0 ? Config.location : _ref$location, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? Config.threshold : _ref$threshold, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? Config.distance : _ref$distance; + + _classCallCheck(this, ExtendedSearch); + + this.query = null; + this.options = { + isCaseSensitive: isCaseSensitive, + includeMatches: includeMatches, + minMatchCharLength: minMatchCharLength, + findAllMatches: findAllMatches, + ignoreLocation: ignoreLocation, + location: location, + threshold: threshold, + distance: distance + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.query = parseQuery(this.pattern, this.options); + } + + _createClass(ExtendedSearch, [{ + key: "searchIn", + value: function searchIn(text) { + var query = this.query; + + if (!query) { + return { + isMatch: false, + score: 1 + }; + } + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + isCaseSensitive = _this$options.isCaseSensitive; + text = isCaseSensitive ? text : text.toLowerCase(); + var numMatches = 0; + var allIndices = []; + var totalScore = 0; // ORs + + for (var i = 0, qLen = query.length; i < qLen; i += 1) { + var searchers = query[i]; // Reset indices + + allIndices.length = 0; + numMatches = 0; // ANDs + + for (var j = 0, pLen = searchers.length; j < pLen; j += 1) { + var searcher = searchers[j]; + + var _searcher$search = searcher.search(text), + isMatch = _searcher$search.isMatch, + indices = _searcher$search.indices, + score = _searcher$search.score; + + if (isMatch) { + numMatches += 1; + totalScore += score; + + if (includeMatches) { + var type = searcher.constructor.type; + + if (MultiMatchSet.has(type)) { + allIndices = [].concat(_toConsumableArray(allIndices), _toConsumableArray(indices)); + } else { + allIndices.push(indices); + } + } + } else { + totalScore = 0; + numMatches = 0; + allIndices.length = 0; + break; + } + } // OR condition, so if TRUE, return + + + if (numMatches) { + var result = { + isMatch: true, + score: totalScore / numMatches + }; + + if (includeMatches) { + result.indices = allIndices; + } + + return result; + } + } // Nothing was matched + + + return { + isMatch: false, + score: 1 + }; + } + }], [{ + key: "condition", + value: function condition(_, options) { + return options.useExtendedSearch; + } + }]); + + return ExtendedSearch; + }(); + + var registeredSearchers = []; + function register() { + registeredSearchers.push.apply(registeredSearchers, arguments); + } + function createSearcher(pattern, options) { + for (var i = 0, len = registeredSearchers.length; i < len; i += 1) { + var searcherClass = registeredSearchers[i]; + + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + + return new BitapSearch(pattern, options); + } + + var LogicalOperator = { + AND: '$and', + OR: '$or' + }; + var KeyType = { + PATH: '$path', + PATTERN: '$val' + }; + + var isExpression = function isExpression(query) { + return !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); + }; + + var isPath = function isPath(query) { + return !!query[KeyType.PATH]; + }; + + var isLeaf = function isLeaf(query) { + return !isArray(query) && isObject(query) && !isExpression(query); + }; + + var convertToExplicit = function convertToExplicit(query) { + return _defineProperty({}, LogicalOperator.AND, Object.keys(query).map(function (key) { + return _defineProperty({}, key, query[key]); + })); + }; // When `auto` is `true`, the parse function will infer and initialize and add + // the appropriate `Searcher` instance + + + function parse(query, options) { + var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref3$auto = _ref3.auto, + auto = _ref3$auto === void 0 ? true : _ref3$auto; + + var next = function next(query) { + var keys = Object.keys(query); + var isQueryPath = isPath(query); + + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + + if (isLeaf(query)) { + var key = isQueryPath ? query[KeyType.PATH] : keys[0]; + var pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + + var obj = { + keyId: createKeyId(key), + pattern: pattern + }; + + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + + return obj; + } + + var node = { + children: [], + operator: keys[0] + }; + keys.forEach(function (key) { + var value = query[key]; + + if (isArray(value)) { + value.forEach(function (item) { + node.children.push(next(item)); + }); + } + }); + return node; + }; + + if (!isExpression(query)) { + query = convertToExplicit(query); + } + + return next(query); + } + + function computeScore$1(results, _ref) { + var _ref$ignoreFieldNorm = _ref.ignoreFieldNorm, + ignoreFieldNorm = _ref$ignoreFieldNorm === void 0 ? Config.ignoreFieldNorm : _ref$ignoreFieldNorm; + results.forEach(function (result) { + var totalScore = 1; + result.matches.forEach(function (_ref2) { + var key = _ref2.key, + norm = _ref2.norm, + score = _ref2.score; + var weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); + } + + function transformMatches(result, data) { + var matches = result.matches; + data.matches = []; + + if (!isDefined(matches)) { + return; + } + + matches.forEach(function (match) { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + + var indices = match.indices, + value = match.value; + var obj = { + indices: indices, + value: value + }; + + if (match.key) { + obj.key = match.key.src; + } + + if (match.idx > -1) { + obj.refIndex = match.idx; + } + + data.matches.push(obj); + }); + } + + function transformScore(result, data) { + data.score = result.score; + } + + function format(results, docs) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? Config.includeMatches : _ref$includeMatches, + _ref$includeScore = _ref.includeScore, + includeScore = _ref$includeScore === void 0 ? Config.includeScore : _ref$includeScore; + + var transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(function (result) { + var idx = result.idx; + var data = { + item: docs[idx], + refIndex: idx + }; + + if (transformers.length) { + transformers.forEach(function (transformer) { + transformer(result, data); + }); + } + + return data; + }); + } + + var Fuse = /*#__PURE__*/function () { + function Fuse(docs) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var index = arguments.length > 2 ? arguments[2] : undefined; + + _classCallCheck(this, Fuse); + + this.options = _objectSpread2({}, Config, {}, options); + + if (this.options.useExtendedSearch && !true) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + + _createClass(Fuse, [{ + key: "setCollection", + value: function setCollection(docs, index) { + this._docs = docs; + + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn + }); + } + }, { + key: "add", + value: function add(doc) { + if (!isDefined(doc)) { + return; + } + + this._docs.push(doc); + + this._myIndex.add(doc); + } + }, { + key: "remove", + value: function remove() { + var predicate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { + return ( + /* doc, idx */ + false + ); + }; + var results = []; + + for (var i = 0, len = this._docs.length; i < len; i += 1) { + var doc = this._docs[i]; + + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + + return results; + } + }, { + key: "removeAt", + value: function removeAt(idx) { + this._docs.splice(idx, 1); + + this._myIndex.removeAt(idx); + } + }, { + key: "getIndex", + value: function getIndex() { + return this._myIndex; + } + }, { + key: "search", + value: function search(query) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$limit = _ref.limit, + limit = _ref$limit === void 0 ? -1 : _ref$limit; + + var _this$options = this.options, + includeMatches = _this$options.includeMatches, + includeScore = _this$options.includeScore, + shouldSort = _this$options.shouldSort, + sortFn = _this$options.sortFn, + ignoreFieldNorm = _this$options.ignoreFieldNorm; + var results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore$1(results, { + ignoreFieldNorm: ignoreFieldNorm + }); + + if (shouldSort) { + results.sort(sortFn); + } + + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + + return format(results, this._docs, { + includeMatches: includeMatches, + includeScore: includeScore + }); + } + }, { + key: "_searchStringList", + value: function _searchStringList(query) { + var searcher = createSearcher(query, this.options); + var records = this._myIndex.records; + var results = []; // Iterate over every string in the index + + records.forEach(function (_ref2) { + var text = _ref2.v, + idx = _ref2.i, + norm = _ref2.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn = searcher.searchIn(text), + isMatch = _searcher$searchIn.isMatch, + score = _searcher$searchIn.score, + indices = _searcher$searchIn.indices; + + if (isMatch) { + results.push({ + item: text, + idx: idx, + matches: [{ + score: score, + value: text, + norm: norm, + indices: indices + }] + }); + } + }); + return results; + } + }, { + key: "_searchLogical", + value: function _searchLogical(query) { + var _this = this; + + var expression = parse(query, this.options); + + var evaluate = function evaluate(node, item, idx) { + if (!node.children) { + var keyId = node.keyId, + searcher = node.searcher; + + var matches = _this._findMatches({ + key: _this._keyStore.get(keyId), + value: _this._myIndex.getValueForItemAtKeyId(item, keyId), + searcher: searcher + }); + + if (matches && matches.length) { + return [{ + idx: idx, + item: item, + matches: matches + }]; + } + + return []; + } + /*eslint indent: [2, 2, {"SwitchCase": 1}]*/ + + + switch (node.operator) { + case LogicalOperator.AND: + { + var res = []; + + for (var i = 0, len = node.children.length; i < len; i += 1) { + var child = node.children[i]; + var result = evaluate(child, item, idx); + + if (result.length) { + res.push.apply(res, _toConsumableArray(result)); + } else { + return []; + } + } + + return res; + } + + case LogicalOperator.OR: + { + var _res = []; + + for (var _i = 0, _len = node.children.length; _i < _len; _i += 1) { + var _child = node.children[_i]; + + var _result = evaluate(_child, item, idx); + + if (_result.length) { + _res.push.apply(_res, _toConsumableArray(_result)); + + break; + } + } + + return _res; + } + } + }; + + var records = this._myIndex.records; + var resultMap = {}; + var results = []; + records.forEach(function (_ref3) { + var item = _ref3.$, + idx = _ref3.i; + + if (isDefined(item)) { + var expResults = evaluate(expression, item, idx); + + if (expResults.length) { + // Dedupe when adding + if (!resultMap[idx]) { + resultMap[idx] = { + idx: idx, + item: item, + matches: [] + }; + results.push(resultMap[idx]); + } + + expResults.forEach(function (_ref4) { + var _resultMap$idx$matche; + + var matches = _ref4.matches; + + (_resultMap$idx$matche = resultMap[idx].matches).push.apply(_resultMap$idx$matche, _toConsumableArray(matches)); + }); + } + } + }); + return results; + } + }, { + key: "_searchObjectList", + value: function _searchObjectList(query) { + var _this2 = this; + + var searcher = createSearcher(query, this.options); + var _this$_myIndex = this._myIndex, + keys = _this$_myIndex.keys, + records = _this$_myIndex.records; + var results = []; // List is Array + + records.forEach(function (_ref5) { + var item = _ref5.$, + idx = _ref5.i; + + if (!isDefined(item)) { + return; + } + + var matches = []; // Iterate over every key (i.e, path), and fetch the value at that key + + keys.forEach(function (key, keyIndex) { + matches.push.apply(matches, _toConsumableArray(_this2._findMatches({ + key: key, + value: item[keyIndex], + searcher: searcher + }))); + }); + + if (matches.length) { + results.push({ + idx: idx, + item: item, + matches: matches + }); + } + }); + return results; + } + }, { + key: "_findMatches", + value: function _findMatches(_ref6) { + var key = _ref6.key, + value = _ref6.value, + searcher = _ref6.searcher; + + if (!isDefined(value)) { + return []; + } + + var matches = []; + + if (isArray(value)) { + value.forEach(function (_ref7) { + var text = _ref7.v, + idx = _ref7.i, + norm = _ref7.n; + + if (!isDefined(text)) { + return; + } + + var _searcher$searchIn2 = searcher.searchIn(text), + isMatch = _searcher$searchIn2.isMatch, + score = _searcher$searchIn2.score, + indices = _searcher$searchIn2.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + idx: idx, + norm: norm, + indices: indices + }); + } + }); + } else { + var text = value.v, + norm = value.n; + + var _searcher$searchIn3 = searcher.searchIn(text), + isMatch = _searcher$searchIn3.isMatch, + score = _searcher$searchIn3.score, + indices = _searcher$searchIn3.indices; + + if (isMatch) { + matches.push({ + score: score, + key: key, + value: text, + norm: norm, + indices: indices + }); + } + } + + return matches; + } + }]); + + return Fuse; + }(); + + Fuse.version = '6.4.6'; + Fuse.createIndex = createIndex; + Fuse.parseIndex = parseIndex; + Fuse.config = Config; + + { + Fuse.parseQuery = parse; + } + + { + register(ExtendedSearch); + } + + return Fuse; + +}))); diff --git a/node_modules/fuse.js/dist/fuse.min.js b/node_modules/fuse.js/dist/fuse.min.js new file mode 100644 index 0000000..7def598 --- /dev/null +++ b/node_modules/fuse.js/dist/fuse.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var e,t;e=this,t=function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:3,t=new Map,n=Math.pow(10,e);return{get:function(e){var r=e.match(I).length;if(t.has(r))return t.get(r);var i=1/Math.sqrt(r),o=parseFloat(Math.round(i*n)/n);return t.set(r,o),o},clear:function(){t.clear()}}}var E=function(){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.getFn,i=void 0===r?A.getFn:r;t(this,e),this.norm=C(3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return r(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?A.getFn:r,o=new E({getFn:i});return o.setKeys(e.map(_)),o.setSources(t),o.create(),o}function R(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?A.distance:s,h=t.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r/e.length;if(f)return l;var d=Math.abs(a-o);return u?l+d/u:d?1:l}function F(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:A.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}function P(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?A.location:o,a=i.threshold,s=void 0===a?A.threshold:a,u=i.distance,h=void 0===u?A.distance:u,f=i.includeMatches,l=void 0===f?A.includeMatches:f,d=i.findAllMatches,v=void 0===d?A.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?A.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?A.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?A.ignoreLocation:k;if(t(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:l,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?n:n.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){r.chunks.push({pattern:e,alphabet:P(e),startIndex:t})},x=this.pattern.length;if(x>32){for(var L=0,S=x%32,w=x-S;L3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?A.location:i,c=r.distance,a=void 0===c?A.distance:c,s=r.threshold,u=void 0===s?A.threshold:s,h=r.findAllMatches,f=void 0===h?A.findAllMatches:h,l=r.minMatchCharLength,d=void 0===l?A.minMatchCharLength:l,v=r.includeMatches,g=void 0===v?A.includeMatches:v,y=r.ignoreLocation,p=void 0===y?A.ignoreLocation:y;if(t.length>32)throw new Error(L(32));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,S=b,w=d>1||g,_=w?Array(M):[];(m=e.indexOf(t,S))>-1;){var O=R(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),S=m+k,w)for(var j=0;j=K;J-=1){var T=J-1,U=n[e.charAt(T)];if(w&&(_[T]=+!!U),W[J]=(W[J+1]<<1|1)&U,P&&(W[J]|=(I[J+1]|I[J])<<1|1|I[J+1]),W[J]&$&&(C=R(t,{errors:P,currentLocation:T,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(S=T)<=b)break;K=Math.max(1,2*b-S)}}var V=R(t,{errors:P+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p});if(V>x)break;I=W}var B={isMatch:S>=0,score:Math.max(.001,C)};if(w){var G=F(_,d);G.length?g&&(B.indices=G):B.isMatch=!1}return B}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:f}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(l(d),l(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),D=function(){function e(n){t(this,e),this.pattern=n}return r(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return z(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return z(e,this.singleRegex)}}]),e}();function z(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),i}(D),q=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),i}(D),W=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),i}(D),J=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),i}(D),T=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),i}(D),U=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),i}(D),V=function(e){a(i,e);var n=f(i);function i(e){var r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?A.location:c,s=o.threshold,u=void 0===s?A.threshold:s,h=o.distance,f=void 0===h?A.distance:h,l=o.includeMatches,d=void 0===l?A.includeMatches:l,v=o.findAllMatches,g=void 0===v?A.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?A.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?A.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?A.ignoreLocation:M;return t(this,i),(r=n.call(this,e))._bitapSearch=new N(e,{location:a,threshold:u,distance:f,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),r}return r(i,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),i}(D),B=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),i}(D),G=[K,B,W,J,U,T,q,V],H=G.length,Q=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;function X(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Q).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=r.isCaseSensitive,o=void 0===i?A.isCaseSensitive:i,c=r.includeMatches,a=void 0===c?A.includeMatches:c,s=r.minMatchCharLength,u=void 0===s?A.minMatchCharLength:s,h=r.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r.findAllMatches,d=void 0===l?A.findAllMatches:l,v=r.location,g=void 0===v?A.location:v,y=r.threshold,p=void 0===y?A.threshold:y,m=r.distance,k=void 0===m?A.distance:m;t(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:f,location:g,threshold:p,distance:k},this.pattern=o?n:n.toLowerCase(),this.query=X(this.pattern,this.options)}return r(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function le(e,t){t.score=e.score}function de(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?A.includeMatches:r,o=n.includeScore,c=void 0===o?A.includeScore:o,a=[];return i&&a.push(fe),c&&a.push(le),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}var ve=function(){function e(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;t(this,e),this.options=c({},A,{},r),this.options.useExtendedSearch,this._keyStore=new w(this.options.keys),this.setCollection(n,i)}return r(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof E))throw new Error("Incorrect 'index' type");this._myIndex=t||$(this.options.keys,this._docs,{getFn:this.options.getFn})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return he(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),de(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=te(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.auto,i=void 0===r||r,o=function e(n){var r=Object.keys(n),o=ae(n);if(!o&&r.length>1&&!ce(n))return e(ue(n));if(se(n)){var c=o?n[ie]:r[0],a=o?n[oe]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return i&&(s.searcher=te(a,t)),s}var u={children:[],operator:r[0]};return r.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return ce(e)||(e=ue(e)),o(e)}(e,this.options),r=this._myIndex.records,i={},o=[];return r.forEach((function(e){var r=e.$,c=e.i;if(k(r)){var a=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}switch(n.operator){case ne:for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?A.getFn:n,i=e.keys,o=e.records,c=new E({getFn:r});return c.setKeys(i),c.setIndexRecords(o),c},ve.config=A,function(){ee.push.apply(ee,arguments)}(Z),ve},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Fuse=t(); \ No newline at end of file diff --git a/node_modules/fuse.js/package.json b/node_modules/fuse.js/package.json new file mode 100644 index 0000000..1a6550c --- /dev/null +++ b/node_modules/fuse.js/package.json @@ -0,0 +1,132 @@ +{ + "_from": "fuse.js", + "_id": "fuse.js@6.4.6", + "_inBundle": false, + "_integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==", + "_location": "/fuse.js", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "fuse.js", + "name": "fuse.js", + "escapedName": "fuse.js", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz", + "_shasum": "62f216c110e5aa22486aff20be7896d19a059b79", + "_spec": "fuse.js", + "_where": "/Users/jasonagus/Desktop/Guide Project", + "author": { + "name": "Kiro Risk", + "email": "kirollos@gmail.com", + "url": "http://kiro.me" + }, + "bugs": { + "url": "https://github.com/krisk/Fuse/issues" + }, + "bundleDependencies": false, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "dependencies": {}, + "deprecated": false, + "description": "Lightweight fuzzy-search", + "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.4", + "@babel/plugin-proposal-object-rest-spread": "7.9.0", + "@babel/preset-env": "7.3.4", + "@babel/preset-typescript": "7.9.0", + "@commitlint/cli": "^9.0.1", + "@commitlint/config-conventional": "^9.0.1", + "@rollup/plugin-node-resolve": "^7.1.1", + "@rollup/plugin-replace": "^2.3.1", + "@types/jest": "25.1.4", + "@vuepress/plugin-google-analytics": "^1.4.0", + "@vuepress/plugin-register-components": "^1.5.2", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.0.5", + "codemirror": "5.52.2", + "eslint": "6.8.0", + "eslint-config-prettier": "6.10.1", + "eslint-plugin-vue": "7.0.0-alpha.0", + "faker": "4.1.0", + "husky": "^4.3.6", + "jest": "^25.1.0", + "prettier": "2.0.2", + "replace-in-file": "^6.1.0", + "rimraf": "3.0.2", + "rollup": "2.1.0", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-copy": "3.3.0", + "standard-version": "^8.0.0", + "terser-webpack-plugin": "2.3.5", + "typescript": "^3.8.3", + "vue-codemirror": "^4.0.6", + "vue-eslint-parser": "^7.0.0", + "vuepress": "^1.4.0", + "vuepress-plugin-element-tabs": "^0.2.8", + "vuepress-plugin-google-adsense": "^0.1.1", + "vuepress-plugin-smooth-scroll": "^0.0.9", + "vuepress-plugin-social-share": "^0.2.1", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + }, + "engines": { + "node": ">=10" + }, + "files": [ + "dist" + ], + "homepage": "http://fusejs.io", + "husky": { + "hooks": { + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "jsdelivr": "./dist/fuse.js", + "keywords": [ + "fuzzy", + "search", + "bitap" + ], + "license": "Apache-2.0", + "main": "./dist/fuse.common.js", + "module": "./dist/fuse.esm.js", + "name": "fuse.js", + "repository": { + "type": "git", + "url": "git+https://github.com/krisk/Fuse.git" + }, + "scripts": { + "build": "rm -r dist && mkdir dist && node ./scripts/build.main.js", + "dev": "rollup -w -c scripts/configs.js --environment TARGET:umd-dev-full", + "dev:cjs": "rollup -w -c scripts/configs.js --environment TARGET:commonjs-full", + "dev:esm": "rollup -w -c scripts/configs.js --environment TARGET:esm-dev-full", + "docs:build": "vuepress build docs", + "docs:bump": "node ./scripts/bump-docs.js", + "docs:dev": "vuepress dev docs", + "docs:release": "./scripts/deploy-docs.sh", + "lint": "eslint src scripts test", + "release": "./scripts/release.sh", + "test": "jest" + }, + "standard-version": { + "scripts": { + "postbump": "yarn build && yarn lint && yarn test 2>/dev/null", + "precommit": "git add dist/*.js dist/*.ts" + } + }, + "typings": "./dist/fuse.d.ts", + "unpkg": "./dist/fuse.js", + "version": "6.4.6" +} diff --git a/node_modules/jaro-winkler/.jshintrc b/node_modules/jaro-winkler/.jshintrc new file mode 100644 index 0000000..fbd365e --- /dev/null +++ b/node_modules/jaro-winkler/.jshintrc @@ -0,0 +1,23 @@ +{ + "camelcase": true, + "curly": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "indent": 2, + "maxdepth": 4, + "maxlen": 80, + "newcap": false, + "noarg": true, + "nonbsp": true, + "quotmark": "single", + "undef": true, + "unused": true, + "eqnull": true, + "strict": true, + "mocha": true, + "node": true, + "globals": { + "define": false + } +} diff --git a/node_modules/jaro-winkler/.npmignore b/node_modules/jaro-winkler/.npmignore new file mode 100644 index 0000000..7595163 --- /dev/null +++ b/node_modules/jaro-winkler/.npmignore @@ -0,0 +1,3 @@ +.DS_Store +node_modules +npm-debug.log diff --git a/node_modules/jaro-winkler/LICENSE b/node_modules/jaro-winkler/LICENSE new file mode 100644 index 0000000..9604d88 --- /dev/null +++ b/node_modules/jaro-winkler/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jordan Thomas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/node_modules/jaro-winkler/README.md b/node_modules/jaro-winkler/README.md new file mode 100644 index 0000000..edaddd3 --- /dev/null +++ b/node_modules/jaro-winkler/README.md @@ -0,0 +1,79 @@ +# Jaro-Winkler + +A string similarity function using the Jaro-Winkler distance metric. Returns a +number between 0 and 1. A 0 being no similarity and a 1 being an exact match. + +Read more about it [on Wikipedia](http://en.wikipedia.org/wiki/Jaro–Winkler_distance). + +## Install + +Install in your Node project using npm as usual; `npm install jaro-winkler`. It +also works in the browser, just include the source within `index.js` in your +project however you prefer. Note that the `distance` function will be added to +the global scope if it's not included with a tool like [Browserify](http://browserify.org). + +## Example Usage + +``` +var distance = require('jaro-winkler'); + +distance('MARTHA', 'MARHTA'); +// 0.961 + +distance('DWAYNE', 'DUANE'); +// 0.84 + +distance('DIXON', 'DICKSONX'); +// 0.814 + + +// Case Insensitive + +distance('MARTHA', 'MARTHA'); +// 1 + +distance('mArThA', 'MaRtHa', { caseSensitive: false }); +// 1 +``` + +## Sample App + +Here's contrived sample of how you might utilize a distance metric in your own +app. This shows how you might offer a helpful message when a user fat-fingers a +command. + +``` +#!/usr/bin/env node + +'use strict'; + +var distance = require('jaro-winkler'); +var commands = ['open', 'close', 'save', 'revert', 'select', 'copy', 'duplicate', 'add', 'subtract']; +var rated = []; +var args = process.argv.slice(2); + +commands.forEach(function(command) { + rated.push({ + command: command, + distance: distance(args[0], command) + }); +}); + +rated.sort(function(a, b) { + if (a.distance < b.distance) { + return 1; + } else if (a.distance > b.distance) { + return -1; + } else { + return 0; + } +}); + +if (rated[0].distance === 1) { + console.log("Running " + rated[0].command + "!"); +} else { + console.log("Did you mean " + rated[0].command + "?"); +} + +process.exit(0); +``` diff --git a/node_modules/jaro-winkler/index.js b/node_modules/jaro-winkler/index.js new file mode 100644 index 0000000..d362479 --- /dev/null +++ b/node_modules/jaro-winkler/index.js @@ -0,0 +1,100 @@ +(function (root) { + 'use strict'; + + function extend(a, b) { + for (var property in b) { + if (b.hasOwnProperty(property)) { + a[property] = b[property]; + } + } + + return a; + } + + function distance(s1, s2, options) { + var m = 0; + var defaults = { caseSensitive: true }; + var settings = extend(defaults, options); + var i; + var j; + + // Exit early if either are empty. + if (s1.length === 0 || s2.length === 0) { + return 0; + } + + // Convert to upper if case-sensitive is false. + if (!settings.caseSensitive) { + s1 = s1.toUpperCase(); + s2 = s2.toUpperCase(); + } + + // Exit early if they're an exact match. + if (s1 === s2) { + return 1; + } + + var range = (Math.floor(Math.max(s1.length, s2.length) / 2)) - 1; + var s1Matches = new Array(s1.length); + var s2Matches = new Array(s2.length); + + for (i = 0; i < s1.length; i++) { + var low = (i >= range) ? i - range : 0; + var high = (i + range <= (s2.length - 1)) ? (i + range) : (s2.length - 1); + + for (j = low; j <= high; j++) { + if (s1Matches[i] !== true && s2Matches[j] !== true && s1[i] === s2[j]) { + ++m; + s1Matches[i] = s2Matches[j] = true; + break; + } + } + } + + // Exit early if no matches were found. + if (m === 0) { + return 0; + } + + // Count the transpositions. + var k = 0; + var numTrans = 0; + + for (i = 0; i < s1.length; i++) { + if (s1Matches[i] === true) { + for (j = k; j < s2.length; j++) { + if (s2Matches[j] === true) { + k = j + 1; + break; + } + } + + if (s1[i] !== s2[j]) { + ++numTrans; + } + } + } + + var weight = (m / s1.length + m / s2.length + (m - (numTrans / 2)) / m) / 3; + var l = 0; + var p = 0.1; + + if (weight > 0.7) { + while (s1[l] === s2[l] && l < 4) { + ++l; + } + + weight = weight + l * p * (1 - weight); + } + + return weight; + } + + if (typeof define === 'function' && define.amd) { + define([], function() {return distance}); + } else if (typeof exports === 'object') { + module.exports = distance; + } else { + root.distance = distance; + } +})(this); diff --git a/node_modules/jaro-winkler/package.json b/node_modules/jaro-winkler/package.json new file mode 100644 index 0000000..000595a --- /dev/null +++ b/node_modules/jaro-winkler/package.json @@ -0,0 +1,61 @@ +{ + "_from": "jaro-winkler", + "_id": "jaro-winkler@0.2.8", + "_inBundle": false, + "_integrity": "sha1-Zyfg0LcJHiQ2+TVt6b+I+tI+U0o=", + "_location": "/jaro-winkler", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "jaro-winkler", + "name": "jaro-winkler", + "escapedName": "jaro-winkler", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/jaro-winkler/-/jaro-winkler-0.2.8.tgz", + "_shasum": "6727e0d0b7091e2436f9356de9bf88fad23e534a", + "_spec": "jaro-winkler", + "_where": "/Users/jasonagus/Desktop/Guide Project", + "author": { + "name": "Jordan Thomas" + }, + "bugs": { + "url": "https://github.com/jordanthomas/jaro-winkler/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A string similarity function using the Jaro-Winkler distance metric.", + "devDependencies": { + "chai": "^1.10.0", + "jshint": "^2.9.2", + "mocha": "^2.5.3" + }, + "homepage": "https://github.com/jordanthomas/jaro-winkler", + "keywords": [ + "distance", + "metric", + "jaro", + "winkler", + "jaro-winkler", + "compare" + ], + "license": "MIT", + "main": "index.js", + "name": "jaro-winkler", + "repository": { + "type": "git", + "url": "git+https://github.com/jordanthomas/jaro-winkler.git" + }, + "scripts": { + "lint": "jshint index.js", + "test": "npm run lint && mocha" + }, + "version": "0.2.8" +} diff --git a/node_modules/jaro-winkler/test/index.html b/node_modules/jaro-winkler/test/index.html new file mode 100644 index 0000000..f82b65d --- /dev/null +++ b/node_modules/jaro-winkler/test/index.html @@ -0,0 +1,19 @@ + + + + Jaro-Winkler Tests + + + +
+ + + + + + + + diff --git a/node_modules/jaro-winkler/test/test.js b/node_modules/jaro-winkler/test/test.js new file mode 100644 index 0000000..af8fa7e --- /dev/null +++ b/node_modules/jaro-winkler/test/test.js @@ -0,0 +1,29 @@ +var distance = typeof require === 'function' ? require('..') : window.distance; +var assert = typeof require === 'function' ? require('chai').assert : window.chai.assert; + +describe('distance', function() { + it('should return a number', function() { + var value = distance('JARO', 'ORAJ'); + assert.typeOf(value, 'number'); + }); + + it('should return the right distance', function() { + // See for basis of expected values: + // http://en.wikipedia.org/wiki/Jaro–Winkler_distance + + var value1 = distance('MARTHA', 'MARHTA'); + assert.closeTo(value1, 0.961, 0.001); + + var value2 = distance('DWAYNE', 'DUANE'); + assert.closeTo(value2, 0.84, 0.001); + + var value3 = distance('DIXON', 'DICKSONX'); + assert.closeTo(value3, 0.814, 0.001); + }); + + it('should ignore case when asked', function() { + var on = distance('jArO', 'JaRo', { caseSensitive: false }); + var off = distance('jaro', 'jaro'); + assert.equal(on, off); + }); +}); diff --git a/node_modules/jaro-winkler/test/vendor/chai.js b/node_modules/jaro-winkler/test/vendor/chai.js new file mode 100644 index 0000000..ffe12c4 --- /dev/null +++ b/node_modules/jaro-winkler/test/vendor/chai.js @@ -0,0 +1,5332 @@ + +;(function(){ + +/** + * Require the module at `name`. + * + * @param {String} name + * @return {Object} exports + * @api public + */ + +function require(name) { + var module = require.modules[name]; + if (!module) throw new Error('failed to require "' + name + '"'); + + if (!('exports' in module) && typeof module.definition === 'function') { + module.client = module.component = true; + module.definition.call(this, module.exports = {}, module); + delete module.definition; + } + + return module.exports; +} + +/** + * Meta info, accessible in the global scope unless you use AMD option. + */ + +require.loader = 'component'; + +/** + * Internal helper object, contains a sorting function for semantiv versioning + */ +require.helper = {}; +require.helper.semVerSort = function(a, b) { + var aArray = a.version.split('.'); + var bArray = b.version.split('.'); + for (var i=0; i bLex ? 1 : -1; + continue; + } else if (aInt > bInt) { + return 1; + } else { + return -1; + } + } + return 0; +} + +/** + * Find and require a module which name starts with the provided name. + * If multiple modules exists, the highest semver is used. + * This function can only be used for remote dependencies. + + * @param {String} name - module name: `user~repo` + * @param {Boolean} returnPath - returns the canonical require path if true, + * otherwise it returns the epxorted module + */ +require.latest = function (name, returnPath) { + function showError(name) { + throw new Error('failed to find latest module of "' + name + '"'); + } + // only remotes with semvers, ignore local files conataining a '/' + var versionRegexp = /(.*)~(.*)@v?(\d+\.\d+\.\d+[^\/]*)$/; + var remoteRegexp = /(.*)~(.*)/; + if (!remoteRegexp.test(name)) showError(name); + var moduleNames = Object.keys(require.modules); + var semVerCandidates = []; + var otherCandidates = []; // for instance: name of the git branch + for (var i=0; i 0) { + var module = semVerCandidates.sort(require.helper.semVerSort).pop().name; + if (returnPath === true) { + return module; + } + return require(module); + } + // if the build contains more than one branch of the same module + // you should not use this funciton + var module = otherCandidates.sort(function(a, b) {return a.name > b.name})[0].name; + if (returnPath === true) { + return module; + } + return require(module); +} + +/** + * Registered modules. + */ + +require.modules = {}; + +/** + * Register module at `name` with callback `definition`. + * + * @param {String} name + * @param {Function} definition + * @api private + */ + +require.register = function (name, definition) { + require.modules[name] = { + definition: definition + }; +}; + +/** + * Define a module's exports immediately with `exports`. + * + * @param {String} name + * @param {Generic} exports + * @api private + */ + +require.define = function (name, exports) { + require.modules[name] = { + exports: exports + }; +}; +require.register("chaijs~assertion-error@1.0.0", function (exports, module) { +/*! + * assertion-error + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Return a function that will copy properties from + * one object to another excluding any originally + * listed. Returned function will create a new `{}`. + * + * @param {String} excluded properties ... + * @return {Function} + */ + +function exclude () { + var excludes = [].slice.call(arguments); + + function excludeProps (res, obj) { + Object.keys(obj).forEach(function (key) { + if (!~excludes.indexOf(key)) res[key] = obj[key]; + }); + } + + return function extendExclude () { + var args = [].slice.call(arguments) + , i = 0 + , res = {}; + + for (; i < args.length; i++) { + excludeProps(res, args[i]); + } + + return res; + }; +}; + +/*! + * Primary Exports + */ + +module.exports = AssertionError; + +/** + * ### AssertionError + * + * An extension of the JavaScript `Error` constructor for + * assertion and validation scenarios. + * + * @param {String} message + * @param {Object} properties to include (optional) + * @param {callee} start stack function (optional) + */ + +function AssertionError (message, _props, ssf) { + var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') + , props = extend(_props || {}); + + // default values + this.message = message || 'Unspecified AssertionError'; + this.showDiff = false; + + // copy from properties + for (var key in props) { + this[key] = props[key]; + } + + // capture stack trace + ssf = ssf || arguments.callee; + if (ssf && Error.captureStackTrace) { + Error.captureStackTrace(this, ssf); + } +} + +/*! + * Inherit from Error.prototype + */ + +AssertionError.prototype = Object.create(Error.prototype); + +/*! + * Statically set name + */ + +AssertionError.prototype.name = 'AssertionError'; + +/*! + * Ensure correct constructor + */ + +AssertionError.prototype.constructor = AssertionError; + +/** + * Allow errors to be converted to JSON for static transfer. + * + * @param {Boolean} include stack (default: `true`) + * @return {Object} object that can be `JSON.stringify` + */ + +AssertionError.prototype.toJSON = function (stack) { + var extend = exclude('constructor', 'toJSON', 'stack') + , props = extend({ name: this.name }, this); + + // include stack if exists and not turned off + if (false !== stack && this.stack) { + props.stack = this.stack; + } + + return props; +}; + +}); + +require.register("chaijs~type-detect@0.1.1", function (exports, module) { +/*! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ + +/*! + * Primary Exports + */ + +var exports = module.exports = getType; + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Array]': 'array' + , '[object RegExp]': 'regexp' + , '[object Function]': 'function' + , '[object Arguments]': 'arguments' + , '[object Date]': 'date' +}; + +/** + * ### typeOf (obj) + * + * Use several different techniques to determine + * the type of object being tested. + * + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ + +function getType (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +} + +exports.Library = Library; + +/** + * ### Library + * + * Create a repository for custom type detection. + * + * ```js + * var lib = new type.Library; + * ``` + * + */ + +function Library () { + this.tests = {}; +} + +/** + * #### .of (obj) + * + * Expose replacement `typeof` detection to the library. + * + * ```js + * if ('string' === lib.of('hello world')) { + * // ... + * } + * ``` + * + * @param {Mixed} object to test + * @return {String} type + */ + +Library.prototype.of = getType; + +/** + * #### .define (type, test) + * + * Add a test to for the `.test()` assertion. + * + * Can be defined as a regular expression: + * + * ```js + * lib.define('int', /^[0-9]+$/); + * ``` + * + * ... or as a function: + * + * ```js + * lib.define('bln', function (obj) { + * if ('boolean' === lib.of(obj)) return true; + * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; + * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); + * return !! ~blns.indexOf(obj); + * }); + * ``` + * + * @param {String} type + * @param {RegExp|Function} test + * @api public + */ + +Library.prototype.define = function (type, test) { + if (arguments.length === 1) return this.tests[type]; + this.tests[type] = test; + return this; +}; + +/** + * #### .test (obj, test) + * + * Assert that an object is of type. Will first + * check natives, and if that does not pass it will + * use the user defined custom tests. + * + * ```js + * assert(lib.test('1', 'int')); + * assert(lib.test('yes', 'bln')); + * ``` + * + * @param {Mixed} object + * @param {String} type + * @return {Boolean} result + * @api public + */ + +Library.prototype.test = function (obj, type) { + if (type === getType(obj)) return true; + var test = this.tests[type]; + + if (test && 'regexp' === getType(test)) { + return test.test(obj); + } else if (test && 'function' === getType(test)) { + return test(obj); + } else { + throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); + } +}; + +}); + +require.register("chaijs~deep-eql@0.1.3", function (exports, module) { +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var type = require('chaijs~type-detect@0.1.1'); + +/*! + * Buffer.isBuffer browser shim + */ + +var Buffer; +try { Buffer = require('buffer').Buffer; } +catch(ex) { + Buffer = {}; + Buffer.isBuffer = function() { return false; } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; + +/** + * Assert super-strict (egal) equality between + * two objects of any type. + * + * @param {Mixed} a + * @param {Mixed} b + * @param {Array} memoised (optional) + * @return {Boolean} equal match + */ + +function deepEqual(a, b, m) { + if (sameValue(a, b)) { + return true; + } else if ('date' === type(a)) { + return dateEqual(a, b); + } else if ('regexp' === type(a)) { + return regexpEqual(a, b); + } else if (Buffer.isBuffer(a)) { + return bufferEqual(a, b); + } else if ('arguments' === type(a)) { + return argumentsEqual(a, b, m); + } else if (!typeEqual(a, b)) { + return false; + } else if (('object' !== type(a) && 'object' !== type(b)) + && ('array' !== type(a) && 'array' !== type(b))) { + return sameValue(a, b); + } else { + return objectEqual(a, b, m); + } +} + +/*! + * Strict (egal) equality test. Ensures that NaN always + * equals NaN and `-0` does not equal `+0`. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} equal match + */ + +function sameValue(a, b) { + if (a === b) return a !== 0 || 1 / a === 1 / b; + return a !== a && b !== b; +} + +/*! + * Compare the types of two given objects and + * return if they are equal. Note that an Array + * has a type of `array` (not `object`) and arguments + * have a type of `arguments` (not `array`/`object`). + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function typeEqual(a, b) { + return type(a) === type(b); +} + +/*! + * Compare two Date objects by asserting that + * the time values are equal using `saveValue`. + * + * @param {Date} a + * @param {Date} b + * @return {Boolean} result + */ + +function dateEqual(a, b) { + if ('date' !== type(b)) return false; + return sameValue(a.getTime(), b.getTime()); +} + +/*! + * Compare two regular expressions by converting them + * to string and checking for `sameValue`. + * + * @param {RegExp} a + * @param {RegExp} b + * @return {Boolean} result + */ + +function regexpEqual(a, b) { + if ('regexp' !== type(b)) return false; + return sameValue(a.toString(), b.toString()); +} + +/*! + * Assert deep equality of two `arguments` objects. + * Unfortunately, these must be sliced to arrays + * prior to test to ensure no bad behavior. + * + * @param {Arguments} a + * @param {Arguments} b + * @param {Array} memoize (optional) + * @return {Boolean} result + */ + +function argumentsEqual(a, b, m) { + if ('arguments' !== type(b)) return false; + a = [].slice.call(a); + b = [].slice.call(b); + return deepEqual(a, b, m); +} + +/*! + * Get enumerable properties of a given object. + * + * @param {Object} a + * @return {Array} property names + */ + +function enumerable(a) { + var res = []; + for (var key in a) res.push(key); + return res; +} + +/*! + * Simple equality for flat iterable objects + * such as Arrays or Node.js buffers. + * + * @param {Iterable} a + * @param {Iterable} b + * @return {Boolean} result + */ + +function iterableEqual(a, b) { + if (a.length !== b.length) return false; + + var i = 0; + var match = true; + + for (; i < a.length; i++) { + if (a[i] !== b[i]) { + match = false; + break; + } + } + + return match; +} + +/*! + * Extension to `iterableEqual` specifically + * for Node.js Buffers. + * + * @param {Buffer} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function bufferEqual(a, b) { + if (!Buffer.isBuffer(b)) return false; + return iterableEqual(a, b); +} + +/*! + * Block for `objectEqual` ensuring non-existing + * values don't get in. + * + * @param {Mixed} object + * @return {Boolean} result + */ + +function isValue(a) { + return a !== null && a !== undefined; +} + +/*! + * Recursively check the equality of two objects. + * Once basic sameness has been established it will + * defer to `deepEqual` for each enumerable key + * in the object. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function objectEqual(a, b, m) { + if (!isValue(a) || !isValue(b)) { + return false; + } + + if (a.prototype !== b.prototype) { + return false; + } + + var i; + if (m) { + for (i = 0; i < m.length; i++) { + if ((m[i][0] === a && m[i][1] === b) + || (m[i][0] === b && m[i][1] === a)) { + return true; + } + } + } else { + m = []; + } + + try { + var ka = enumerable(a); + var kb = enumerable(b); + } catch (ex) { + return false; + } + + ka.sort(); + kb.sort(); + + if (!iterableEqual(ka, kb)) { + return false; + } + + m.push([ a, b ]); + + var key; + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], m)) { + return false; + } + } + + return true; +} + +}); + +require.register("chai", function (exports, module) { +module.exports = require('chai/lib/chai.js'); + +}); + +require.register("chai/lib/chai.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var used = [] + , exports = module.exports = {}; + +/*! + * Chai version + */ + +exports.version = '2.1.0'; + +/*! + * Assertion Error + */ + +exports.AssertionError = require('chaijs~assertion-error@1.0.0'); + +/*! + * Utils for plugins (not exported) + */ + +var util = require('chai/lib/chai/utils/index.js'); + +/** + * # .use(function) + * + * Provides a way to extend the internals of Chai + * + * @param {Function} + * @returns {this} for chaining + * @api public + */ + +exports.use = function (fn) { + if (!~used.indexOf(fn)) { + fn(this, util); + used.push(fn); + } + + return this; +}; + +/*! + * Utility Functions + */ + +exports.util = util; + +/*! + * Configuration + */ + +var config = require('chai/lib/chai/config.js'); +exports.config = config; + +/*! + * Primary `Assertion` prototype + */ + +var assertion = require('chai/lib/chai/assertion.js'); +exports.use(assertion); + +/*! + * Core Assertions + */ + +var core = require('chai/lib/chai/core/assertions.js'); +exports.use(core); + +/*! + * Expect interface + */ + +var expect = require('chai/lib/chai/interface/expect.js'); +exports.use(expect); + +/*! + * Should interface + */ + +var should = require('chai/lib/chai/interface/should.js'); +exports.use(should); + +/*! + * Assert interface + */ + +var assert = require('chai/lib/chai/interface/assert.js'); +exports.use(assert); + +}); + +require.register("chai/lib/chai/assertion.js", function (exports, module) { +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var config = require('chai/lib/chai/config.js'); + +module.exports = function (_chai, util) { + /*! + * Module dependencies. + */ + + var AssertionError = _chai.AssertionError + , flag = util.flag; + + /*! + * Module export. + */ + + _chai.Assertion = Assertion; + + /*! + * Assertion Constructor + * + * Creates object for chaining. + * + * @api private + */ + + function Assertion (obj, msg, stack) { + flag(this, 'ssfi', stack || arguments.callee); + flag(this, 'object', obj); + flag(this, 'message', msg); + } + + Object.defineProperty(Assertion, 'includeStack', { + get: function() { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + return config.includeStack; + }, + set: function(value) { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + config.includeStack = value; + } + }); + + Object.defineProperty(Assertion, 'showDiff', { + get: function() { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + return config.showDiff; + }, + set: function(value) { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + config.showDiff = value; + } + }); + + Assertion.addProperty = function (name, fn) { + util.addProperty(this.prototype, name, fn); + }; + + Assertion.addMethod = function (name, fn) { + util.addMethod(this.prototype, name, fn); + }; + + Assertion.addChainableMethod = function (name, fn, chainingBehavior) { + util.addChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + Assertion.overwriteProperty = function (name, fn) { + util.overwriteProperty(this.prototype, name, fn); + }; + + Assertion.overwriteMethod = function (name, fn) { + util.overwriteMethod(this.prototype, name, fn); + }; + + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + /*! + * ### .assert(expression, message, negateMessage, expected, actual) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {Philosophical} expression to be tested + * @param {String or Function} message or function that returns message to display if fails + * @param {String or Function} negatedMessage or function that returns negatedMessage to display if negated expression fails + * @param {Mixed} expected value (remember to check for negation) + * @param {Mixed} actual (optional) will default to `this.obj` + * @api private + */ + + Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { + var ok = util.test(this, arguments); + if (true !== showDiff) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + var msg = util.getMessage(this, arguments) + , actual = util.getActual(this, arguments); + throw new AssertionError(msg, { + actual: actual + , expected: expected + , showDiff: showDiff + }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); + } + }; + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @api private + */ + + Object.defineProperty(Assertion.prototype, '_obj', + { get: function () { + return flag(this, 'object'); + } + , set: function (val) { + flag(this, 'object', val); + } + }); +}; + +}); + +require.register("chai/lib/chai/config.js", function (exports, module) { +module.exports = { + + /** + * ### config.includeStack + * + * User configurable property, influences whether stack trace + * is included in Assertion error message. Default of false + * suppresses stack trace in the error message. + * + * chai.config.includeStack = true; // enable stack on error + * + * @param {Boolean} + * @api public + */ + + includeStack: false, + + /** + * ### config.showDiff + * + * User configurable property, influences whether or not + * the `showDiff` flag should be included in the thrown + * AssertionErrors. `false` will always be `false`; `true` + * will be true when the assertion has requested a diff + * be shown. + * + * @param {Boolean} + * @api public + */ + + showDiff: true, + + /** + * ### config.truncateThreshold + * + * User configurable property, sets length threshold for actual and + * expected values in assertion errors. If this threshold is exceeded, + * the value is truncated. + * + * Set it to zero if you want to disable truncating altogether. + * + * chai.config.truncateThreshold = 0; // disable truncating + * + * @param {Number} + * @api public + */ + + truncateThreshold: 40 + +}; + +}); + +require.register("chai/lib/chai/core/assertions.js", function (exports, module) { +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, _) { + var Assertion = chai.Assertion + , toString = Object.prototype.toString + , flag = _.flag; + + /** + * ### Language Chains + * + * The following are provided as chainable getters to + * improve the readability of your assertions. They + * do not provide testing capabilities unless they + * have been overwritten by a plugin. + * + * **Chains** + * + * - to + * - be + * - been + * - is + * - that + * - which + * - and + * - has + * - have + * - with + * - at + * - of + * - same + * + * @name language chains + * @api public + */ + + [ 'to', 'be', 'been' + , 'is', 'and', 'has', 'have' + , 'with', 'that', 'which', 'at' + , 'of', 'same' ].forEach(function (chain) { + Assertion.addProperty(chain, function () { + return this; + }); + }); + + /** + * ### .not + * + * Negates any of assertions following in the chain. + * + * expect(foo).to.not.equal('bar'); + * expect(goodFn).to.not.throw(Error); + * expect({ foo: 'baz' }).to.have.property('foo') + * .and.not.equal('bar'); + * + * @name not + * @api public + */ + + Assertion.addProperty('not', function () { + flag(this, 'negate', true); + }); + + /** + * ### .deep + * + * Sets the `deep` flag, later used by the `equal` and + * `property` assertions. + * + * expect(foo).to.deep.equal({ bar: 'baz' }); + * expect({ foo: { bar: { baz: 'quux' } } }) + * .to.have.deep.property('foo.bar.baz', 'quux'); + * + * @name deep + * @api public + */ + + Assertion.addProperty('deep', function () { + flag(this, 'deep', true); + }); + + /** + * ### .any + * + * Sets the `any` flag, (opposite of the `all` flag) + * later used in the `keys` assertion. + * + * expect(foo).to.have.any.keys('bar', 'baz'); + * + * @name any + * @api public + */ + + Assertion.addProperty('any', function () { + flag(this, 'any', true); + flag(this, 'all', false) + }); + + + /** + * ### .all + * + * Sets the `all` flag (opposite of the `any` flag) + * later used by the `keys` assertion. + * + * expect(foo).to.have.all.keys('bar', 'baz'); + * + * @name all + * @api public + */ + + Assertion.addProperty('all', function () { + flag(this, 'all', true); + flag(this, 'any', false); + }); + + /** + * ### .a(type) + * + * The `a` and `an` assertions are aliases that can be + * used either as language chains or to assert a value's + * type. + * + * // typeof + * expect('test').to.be.a('string'); + * expect({ foo: 'bar' }).to.be.an('object'); + * expect(null).to.be.a('null'); + * expect(undefined).to.be.an('undefined'); + * + * // language chain + * expect(foo).to.be.an.instanceof(Foo); + * + * @name a + * @alias an + * @param {String} type + * @param {String} message _optional_ + * @api public + */ + + function an (type, msg) { + if (msg) flag(this, 'message', msg); + type = type.toLowerCase(); + var obj = flag(this, 'object') + , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; + + this.assert( + type === _.type(obj) + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); + } + + Assertion.addChainableMethod('an', an); + Assertion.addChainableMethod('a', an); + + /** + * ### .include(value) + * + * The `include` and `contain` assertions can be used as either property + * based language chains or as methods to assert the inclusion of an object + * in an array or a substring in a string. When used as language chains, + * they toggle the `contains` flag for the `keys` assertion. + * + * expect([1,2,3]).to.include(2); + * expect('foobar').to.contain('foo'); + * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); + * + * @name include + * @alias contain + * @alias includes + * @alias contains + * @param {Object|String|Number} obj + * @param {String} message _optional_ + * @api public + */ + + function includeChainingBehavior () { + flag(this, 'contains', true); + } + + function include (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var expected = false; + if (_.type(obj) === 'array' && _.type(val) === 'object') { + for (var i in obj) { + if (_.eql(obj[i], val)) { + expected = true; + break; + } + } + } else if (_.type(val) === 'object') { + if (!flag(this, 'negate')) { + for (var k in val) new Assertion(obj).property(k, val[k]); + return; + } + var subset = {}; + for (var k in val) subset[k] = obj[k]; + expected = _.eql(subset, val); + } else { + expected = obj && ~obj.indexOf(val); + } + this.assert( + expected + , 'expected #{this} to include ' + _.inspect(val) + , 'expected #{this} to not include ' + _.inspect(val)); + } + + Assertion.addChainableMethod('include', include, includeChainingBehavior); + Assertion.addChainableMethod('contain', include, includeChainingBehavior); + Assertion.addChainableMethod('contains', include, includeChainingBehavior); + Assertion.addChainableMethod('includes', include, includeChainingBehavior); + + /** + * ### .ok + * + * Asserts that the target is truthy. + * + * expect('everthing').to.be.ok; + * expect(1).to.be.ok; + * expect(false).to.not.be.ok; + * expect(undefined).to.not.be.ok; + * expect(null).to.not.be.ok; + * + * @name ok + * @api public + */ + + Assertion.addProperty('ok', function () { + this.assert( + flag(this, 'object') + , 'expected #{this} to be truthy' + , 'expected #{this} to be falsy'); + }); + + /** + * ### .true + * + * Asserts that the target is `true`. + * + * expect(true).to.be.true; + * expect(1).to.not.be.true; + * + * @name true + * @api public + */ + + Assertion.addProperty('true', function () { + this.assert( + true === flag(this, 'object') + , 'expected #{this} to be true' + , 'expected #{this} to be false' + , this.negate ? false : true + ); + }); + + /** + * ### .false + * + * Asserts that the target is `false`. + * + * expect(false).to.be.false; + * expect(0).to.not.be.false; + * + * @name false + * @api public + */ + + Assertion.addProperty('false', function () { + this.assert( + false === flag(this, 'object') + , 'expected #{this} to be false' + , 'expected #{this} to be true' + , this.negate ? true : false + ); + }); + + /** + * ### .null + * + * Asserts that the target is `null`. + * + * expect(null).to.be.null; + * expect(undefined).not.to.be.null; + * + * @name null + * @api public + */ + + Assertion.addProperty('null', function () { + this.assert( + null === flag(this, 'object') + , 'expected #{this} to be null' + , 'expected #{this} not to be null' + ); + }); + + /** + * ### .undefined + * + * Asserts that the target is `undefined`. + * + * expect(undefined).to.be.undefined; + * expect(null).to.not.be.undefined; + * + * @name undefined + * @api public + */ + + Assertion.addProperty('undefined', function () { + this.assert( + undefined === flag(this, 'object') + , 'expected #{this} to be undefined' + , 'expected #{this} not to be undefined' + ); + }); + + /** + * ### .exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi' + * , bar = null + * , baz; + * + * expect(foo).to.exist; + * expect(bar).to.not.exist; + * expect(baz).to.not.exist; + * + * @name exist + * @api public + */ + + Assertion.addProperty('exist', function () { + this.assert( + null != flag(this, 'object') + , 'expected #{this} to exist' + , 'expected #{this} to not exist' + ); + }); + + + /** + * ### .empty + * + * Asserts that the target's length is `0`. For arrays, it checks + * the `length` property. For objects, it gets the count of + * enumerable keys. + * + * expect([]).to.be.empty; + * expect('').to.be.empty; + * expect({}).to.be.empty; + * + * @name empty + * @api public + */ + + Assertion.addProperty('empty', function () { + var obj = flag(this, 'object') + , expected = obj; + + if (Array.isArray(obj) || 'string' === typeof object) { + expected = obj.length; + } else if (typeof obj === 'object') { + expected = Object.keys(obj).length; + } + + this.assert( + !expected + , 'expected #{this} to be empty' + , 'expected #{this} not to be empty' + ); + }); + + /** + * ### .arguments + * + * Asserts that the target is an arguments object. + * + * function test () { + * expect(arguments).to.be.arguments; + * } + * + * @name arguments + * @alias Arguments + * @api public + */ + + function checkArguments () { + var obj = flag(this, 'object') + , type = Object.prototype.toString.call(obj); + this.assert( + '[object Arguments]' === type + , 'expected #{this} to be arguments but got ' + type + , 'expected #{this} to not be arguments' + ); + } + + Assertion.addProperty('arguments', checkArguments); + Assertion.addProperty('Arguments', checkArguments); + + /** + * ### .equal(value) + * + * Asserts that the target is strictly equal (`===`) to `value`. + * Alternately, if the `deep` flag is set, asserts that + * the target is deeply equal to `value`. + * + * expect('hello').to.equal('hello'); + * expect(42).to.equal(42); + * expect(1).to.not.equal(true); + * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); + * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); + * + * @name equal + * @alias equals + * @alias eq + * @alias deep.equal + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEqual (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'deep')) { + return this.eql(val); + } else { + this.assert( + val === obj + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{exp}' + , val + , this._obj + , true + ); + } + } + + Assertion.addMethod('equal', assertEqual); + Assertion.addMethod('equals', assertEqual); + Assertion.addMethod('eq', assertEqual); + + /** + * ### .eql(value) + * + * Asserts that the target is deeply equal to `value`. + * + * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); + * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); + * + * @name eql + * @alias eqls + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEql(obj, msg) { + if (msg) flag(this, 'message', msg); + this.assert( + _.eql(obj, flag(this, 'object')) + , 'expected #{this} to deeply equal #{exp}' + , 'expected #{this} to not deeply equal #{exp}' + , obj + , this._obj + , true + ); + } + + Assertion.addMethod('eql', assertEql); + Assertion.addMethod('eqls', assertEql); + + /** + * ### .above(value) + * + * Asserts that the target is greater than `value`. + * + * expect(10).to.be.above(5); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * + * @name above + * @alias gt + * @alias greaterThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertAbove (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len > n + , 'expected #{this} to have a length above #{exp} but got #{act}' + , 'expected #{this} to not have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj > n + , 'expected #{this} to be above ' + n + , 'expected #{this} to be at most ' + n + ); + } + } + + Assertion.addMethod('above', assertAbove); + Assertion.addMethod('gt', assertAbove); + Assertion.addMethod('greaterThan', assertAbove); + + /** + * ### .least(value) + * + * Asserts that the target is greater than or equal to `value`. + * + * expect(10).to.be.at.least(10); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.least(2); + * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); + * + * @name least + * @alias gte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertLeast (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= n + , 'expected #{this} to have a length at least #{exp} but got #{act}' + , 'expected #{this} to have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj >= n + , 'expected #{this} to be at least ' + n + , 'expected #{this} to be below ' + n + ); + } + } + + Assertion.addMethod('least', assertLeast); + Assertion.addMethod('gte', assertLeast); + + /** + * ### .below(value) + * + * Asserts that the target is less than `value`. + * + * expect(5).to.be.below(10); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * + * @name below + * @alias lt + * @alias lessThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertBelow (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len < n + , 'expected #{this} to have a length below #{exp} but got #{act}' + , 'expected #{this} to not have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj < n + , 'expected #{this} to be below ' + n + , 'expected #{this} to be at least ' + n + ); + } + } + + Assertion.addMethod('below', assertBelow); + Assertion.addMethod('lt', assertBelow); + Assertion.addMethod('lessThan', assertBelow); + + /** + * ### .most(value) + * + * Asserts that the target is less than or equal to `value`. + * + * expect(5).to.be.at.most(5); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.most(4); + * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); + * + * @name most + * @alias lte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertMost (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len <= n + , 'expected #{this} to have a length at most #{exp} but got #{act}' + , 'expected #{this} to have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj <= n + , 'expected #{this} to be at most ' + n + , 'expected #{this} to be above ' + n + ); + } + } + + Assertion.addMethod('most', assertMost); + Assertion.addMethod('lte', assertMost); + + /** + * ### .within(start, finish) + * + * Asserts that the target is within a range. + * + * expect(7).to.be.within(5,10); + * + * Can also be used in conjunction with `length` to + * assert a length range. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name within + * @param {Number} start lowerbound inclusive + * @param {Number} finish upperbound inclusive + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('within', function (start, finish, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , range = start + '..' + finish; + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= start && len <= finish + , 'expected #{this} to have a length within ' + range + , 'expected #{this} to not have a length within ' + range + ); + } else { + this.assert( + obj >= start && obj <= finish + , 'expected #{this} to be within ' + range + , 'expected #{this} to not be within ' + range + ); + } + }); + + /** + * ### .instanceof(constructor) + * + * Asserts that the target is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , Chai = new Tea('chai'); + * + * expect(Chai).to.be.an.instanceof(Tea); + * expect([ 1, 2, 3 ]).to.be.instanceof(Array); + * + * @name instanceof + * @param {Constructor} constructor + * @param {String} message _optional_ + * @alias instanceOf + * @api public + */ + + function assertInstanceOf (constructor, msg) { + if (msg) flag(this, 'message', msg); + var name = _.getName(constructor); + this.assert( + flag(this, 'object') instanceof constructor + , 'expected #{this} to be an instance of ' + name + , 'expected #{this} to not be an instance of ' + name + ); + }; + + Assertion.addMethod('instanceof', assertInstanceOf); + Assertion.addMethod('instanceOf', assertInstanceOf); + + /** + * ### .property(name, [value]) + * + * Asserts that the target has a property `name`, optionally asserting that + * the value of that property is strictly equal to `value`. + * If the `deep` flag is set, you can use dot- and bracket-notation for deep + * references into objects and arrays. + * + * // simple referencing + * var obj = { foo: 'bar' }; + * expect(obj).to.have.property('foo'); + * expect(obj).to.have.property('foo', 'bar'); + * + * // deep referencing + * var deepObj = { + * green: { tea: 'matcha' } + * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] + * }; + + * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); + * + * You can also use an array as the starting point of a `deep.property` + * assertion, or traverse nested arrays. + * + * var arr = [ + * [ 'chai', 'matcha', 'konacha' ] + * , [ { tea: 'chai' } + * , { tea: 'matcha' } + * , { tea: 'konacha' } ] + * ]; + * + * expect(arr).to.have.deep.property('[0][1]', 'matcha'); + * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); + * + * Furthermore, `property` changes the subject of the assertion + * to be the value of that property from the original object. This + * permits for further chainable assertions on that property. + * + * expect(obj).to.have.property('foo') + * .that.is.a('string'); + * expect(deepObj).to.have.property('green') + * .that.is.an('object') + * .that.deep.equals({ tea: 'matcha' }); + * expect(deepObj).to.have.property('teas') + * .that.is.an('array') + * .with.deep.property('[2]') + * .that.deep.equals({ tea: 'konacha' }); + * + * @name property + * @alias deep.property + * @param {String} name + * @param {Mixed} value (optional) + * @param {String} message _optional_ + * @returns value of property for chaining + * @api public + */ + + Assertion.addMethod('property', function (name, val, msg) { + if (msg) flag(this, 'message', msg); + + var isDeep = !!flag(this, 'deep') + , descriptor = isDeep ? 'deep property ' : 'property ' + , negate = flag(this, 'negate') + , obj = flag(this, 'object') + , pathInfo = isDeep ? _.getPathInfo(name, obj) : null + , hasProperty = isDeep + ? pathInfo.exists + : _.hasProperty(name, obj) + , value = isDeep + ? pathInfo.value + : obj[name]; + + if (negate && undefined !== val) { + if (undefined === value) { + msg = (msg != null) ? msg + ': ' : ''; + throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); + } + } else { + this.assert( + hasProperty + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + , 'expected #{this} to not have ' + descriptor + _.inspect(name)); + } + + if (undefined !== val) { + this.assert( + val === value + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' + , val + , value + ); + } + + flag(this, 'object', value); + }); + + + /** + * ### .ownProperty(name) + * + * Asserts that the target has an own property `name`. + * + * expect('test').to.have.ownProperty('length'); + * + * @name ownProperty + * @alias haveOwnProperty + * @param {String} name + * @param {String} message _optional_ + * @api public + */ + + function assertOwnProperty (name, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + obj.hasOwnProperty(name) + , 'expected #{this} to have own property ' + _.inspect(name) + , 'expected #{this} to not have own property ' + _.inspect(name) + ); + } + + Assertion.addMethod('ownProperty', assertOwnProperty); + Assertion.addMethod('haveOwnProperty', assertOwnProperty); + + /** + * ### .length(value) + * + * Asserts that the target's `length` property has + * the expected value. + * + * expect([ 1, 2, 3]).to.have.length(3); + * expect('foobar').to.have.length(6); + * + * Can also be used as a chain precursor to a value + * comparison for the length property. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name length + * @alias lengthOf + * @param {Number} length + * @param {String} message _optional_ + * @api public + */ + + function assertLengthChain () { + flag(this, 'doLength', true); + } + + function assertLength (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + + this.assert( + len == n + , 'expected #{this} to have a length of #{exp} but got #{act}' + , 'expected #{this} to not have a length of #{act}' + , n + , len + ); + } + + Assertion.addChainableMethod('length', assertLength, assertLengthChain); + Assertion.addMethod('lengthOf', assertLength); + + /** + * ### .match(regexp) + * + * Asserts that the target matches a regular expression. + * + * expect('foobar').to.match(/^foo/); + * + * @name match + * @param {RegExp} RegularExpression + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('match', function (re, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + re.exec(obj) + , 'expected #{this} to match ' + re + , 'expected #{this} not to match ' + re + ); + }); + + /** + * ### .string(string) + * + * Asserts that the string target contains another string. + * + * expect('foobar').to.have.string('bar'); + * + * @name string + * @param {String} string + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('string', function (str, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('string'); + + this.assert( + ~obj.indexOf(str) + , 'expected #{this} to contain ' + _.inspect(str) + , 'expected #{this} to not contain ' + _.inspect(str) + ); + }); + + + /** + * ### .keys(key1, [key2], [...]) + * + * Asserts that the target contains any or all of the passed-in keys. + * Use in combination with `any`, `all`, `contains`, or `have` will affect + * what will pass. + * + * When used in conjunction with `any`, at least one key that is passed + * in must exist in the target object. This is regardless whether or not + * the `have` or `contain` qualifiers are used. Note, either `any` or `all` + * should be used in the assertion. If neither are used, the assertion is + * defaulted to `all`. + * + * When both `all` and `contain` are used, the target object must have at + * least all of the passed-in keys but may have more keys not listed. + * + * When both `all` and `have` are used, the target object must both contain + * all of the passed-in keys AND the number of keys in the target object must + * match the number of keys passed in (in other words, a target object must + * have all and only all of the passed-in keys). + * + * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz'); + * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo'); + * expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz'); + * expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']); + * expect({ foo: 1, bar: 2 }).to.contain.any.keys({'foo': 6}); + * expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']); + * expect({ foo: 1, bar: 2 }).to.have.all.keys({'bar': 6, 'foo', 7}); + * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']); + * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys([{'bar': 6}}]); + * + * + * @name keys + * @alias key + * @param {String...|Array|Object} keys + * @api public + */ + + function assertKeys (keys) { + var obj = flag(this, 'object') + , str + , ok = true + , mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments'; + + switch (_.type(keys)) { + case "array": + if (arguments.length > 1) throw (new Error(mixedArgsMsg)); + break; + case "object": + if (arguments.length > 1) throw (new Error(mixedArgsMsg)); + keys = Object.keys(keys); + break; + default: + keys = Array.prototype.slice.call(arguments); + } + + if (!keys.length) throw new Error('keys required'); + + var actual = Object.keys(obj) + , expected = keys + , len = keys.length + , any = flag(this, 'any') + , all = flag(this, 'all'); + + if (!any && !all) { + all = true; + } + + // Has any + if (any) { + var intersection = expected.filter(function(key) { + return ~actual.indexOf(key); + }); + ok = intersection.length > 0; + } + + // Has all + if (all) { + ok = keys.every(function(key){ + return ~actual.indexOf(key); + }); + if (!flag(this, 'negate') && !flag(this, 'contains')) { + ok = ok && keys.length == actual.length; + } + } + + // Key string + if (len > 1) { + keys = keys.map(function(key){ + return _.inspect(key); + }); + var last = keys.pop(); + if (all) { + str = keys.join(', ') + ', and ' + last; + } + if (any) { + str = keys.join(', ') + ', or ' + last; + } + } else { + str = _.inspect(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected #{this} to ' + str + , 'expected #{this} to not ' + str + , expected.slice(0).sort() + , actual.sort() + , true + ); + } + + Assertion.addMethod('keys', assertKeys); + Assertion.addMethod('key', assertKeys); + + /** + * ### .throw(constructor) + * + * Asserts that the function target will throw a specific error, or specific type of error + * (as determined using `instanceof`), optionally with a RegExp or string inclusion test + * for the error's message. + * + * var err = new ReferenceError('This is a bad function.'); + * var fn = function () { throw err; } + * expect(fn).to.throw(ReferenceError); + * expect(fn).to.throw(Error); + * expect(fn).to.throw(/bad function/); + * expect(fn).to.not.throw('good function'); + * expect(fn).to.throw(ReferenceError, /bad function/); + * expect(fn).to.throw(err); + * expect(fn).to.not.throw(new RangeError('Out of range.')); + * + * Please note that when a throw expectation is negated, it will check each + * parameter independently, starting with error constructor type. The appropriate way + * to check for the existence of a type of error but for a message that does not match + * is to use `and`. + * + * expect(fn).to.throw(ReferenceError) + * .and.not.throw(/good function/); + * + * @name throw + * @alias throws + * @alias Throw + * @param {ErrorConstructor} constructor + * @param {String|RegExp} expected error message + * @param {String} message _optional_ + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) + * @api public + */ + + function assertThrows (constructor, errMsg, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('function'); + + var thrown = false + , desiredError = null + , name = null + , thrownError = null; + + if (arguments.length === 0) { + errMsg = null; + constructor = null; + } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { + errMsg = constructor; + constructor = null; + } else if (constructor && constructor instanceof Error) { + desiredError = constructor; + constructor = null; + errMsg = null; + } else if (typeof constructor === 'function') { + name = constructor.prototype.name || constructor.name; + if (name === 'Error' && constructor !== Error) { + name = (new constructor()).name; + } + } else { + constructor = null; + } + + try { + obj(); + } catch (err) { + // first, check desired error + if (desiredError) { + this.assert( + err === desiredError + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (err instanceof Error ? err.toString() : err) + ); + + flag(this, 'object', err); + return this; + } + + // next, check constructor + if (constructor) { + this.assert( + err instanceof constructor + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp} but #{act} was thrown' + , name + , (err instanceof Error ? err.toString() : err) + ); + + if (!errMsg) { + flag(this, 'object', err); + return this; + } + } + + // next, check message + var message = 'object' === _.type(err) && "message" in err + ? err.message + : '' + err; + + if ((message != null) && errMsg && errMsg instanceof RegExp) { + this.assert( + errMsg.exec(message) + , 'expected #{this} to throw error matching #{exp} but got #{act}' + , 'expected #{this} to throw error not matching #{exp}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else if ((message != null) && errMsg && 'string' === typeof errMsg) { + this.assert( + ~message.indexOf(errMsg) + , 'expected #{this} to throw error including #{exp} but got #{act}' + , 'expected #{this} to throw error not including #{act}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else { + thrown = true; + thrownError = err; + } + } + + var actuallyGot = '' + , expectedThrown = name !== null + ? name + : desiredError + ? '#{exp}' //_.inspect(desiredError) + : 'an error'; + + if (thrown) { + actuallyGot = ' but #{act} was thrown' + } + + this.assert( + thrown === true + , 'expected #{this} to throw ' + expectedThrown + actuallyGot + , 'expected #{this} to not throw ' + expectedThrown + actuallyGot + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (thrownError instanceof Error ? thrownError.toString() : thrownError) + ); + + flag(this, 'object', thrownError); + }; + + Assertion.addMethod('throw', assertThrows); + Assertion.addMethod('throws', assertThrows); + Assertion.addMethod('Throw', assertThrows); + + /** + * ### .respondTo(method) + * + * Asserts that the object or class target will respond to a method. + * + * Klass.prototype.bar = function(){}; + * expect(Klass).to.respondTo('bar'); + * expect(obj).to.respondTo('bar'); + * + * To check if a constructor will respond to a static function, + * set the `itself` flag. + * + * Klass.baz = function(){}; + * expect(Klass).itself.to.respondTo('baz'); + * + * @name respondTo + * @param {String} method + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('respondTo', function (method, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , itself = flag(this, 'itself') + , context = ('function' === _.type(obj) && !itself) + ? obj.prototype[method] + : obj[method]; + + this.assert( + 'function' === typeof context + , 'expected #{this} to respond to ' + _.inspect(method) + , 'expected #{this} to not respond to ' + _.inspect(method) + ); + }); + + /** + * ### .itself + * + * Sets the `itself` flag, later used by the `respondTo` assertion. + * + * function Foo() {} + * Foo.bar = function() {} + * Foo.prototype.baz = function() {} + * + * expect(Foo).itself.to.respondTo('bar'); + * expect(Foo).itself.not.to.respondTo('baz'); + * + * @name itself + * @api public + */ + + Assertion.addProperty('itself', function () { + flag(this, 'itself', true); + }); + + /** + * ### .satisfy(method) + * + * Asserts that the target passes a given truth test. + * + * expect(1).to.satisfy(function(num) { return num > 0; }); + * + * @name satisfy + * @param {Function} matcher + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('satisfy', function (matcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var result = matcher(obj); + this.assert( + result + , 'expected #{this} to satisfy ' + _.objDisplay(matcher) + , 'expected #{this} to not satisfy' + _.objDisplay(matcher) + , this.negate ? false : true + , result + ); + }); + + /** + * ### .closeTo(expected, delta) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * expect(1.5).to.be.closeTo(1, 0.5); + * + * @name closeTo + * @param {Number} expected + * @param {Number} delta + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('closeTo', function (expected, delta, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + + new Assertion(obj, msg).is.a('number'); + if (_.type(expected) !== 'number' || _.type(delta) !== 'number') { + throw new Error('the arguments to closeTo must be numbers'); + } + + this.assert( + Math.abs(obj - expected) <= delta + , 'expected #{this} to be close to ' + expected + ' +/- ' + delta + , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta + ); + }); + + function isSubsetOf(subset, superset, cmp) { + return subset.every(function(elem) { + if (!cmp) return superset.indexOf(elem) !== -1; + + return superset.some(function(elem2) { + return cmp(elem, elem2); + }); + }) + } + + /** + * ### .members(set) + * + * Asserts that the target is a superset of `set`, + * or that the target and `set` have the same strictly-equal (===) members. + * Alternately, if the `deep` flag is set, set members are compared for deep + * equality. + * + * expect([1, 2, 3]).to.include.members([3, 2]); + * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); + * + * expect([4, 2]).to.have.members([2, 4]); + * expect([5, 2]).to.not.have.members([5, 2, 1]); + * + * expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]); + * + * @name members + * @param {Array} set + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('members', function (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + + new Assertion(obj).to.be.an('array'); + new Assertion(subset).to.be.an('array'); + + var cmp = flag(this, 'deep') ? _.eql : undefined; + + if (flag(this, 'contains')) { + return this.assert( + isSubsetOf(subset, obj, cmp) + , 'expected #{this} to be a superset of #{act}' + , 'expected #{this} to not be a superset of #{act}' + , obj + , subset + ); + } + + this.assert( + isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp) + , 'expected #{this} to have the same members as #{act}' + , 'expected #{this} to not have the same members as #{act}' + , obj + , subset + ); + }); + + /** + * ### .change(function) + * + * Asserts that a function changes an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 3 }; + * var noChangeFn = function() { return 'foo' + 'bar'; } + * expect(fn).to.change(obj, 'val'); + * expect(noChangFn).to.not.change(obj, 'val') + * + * @name change + * @alias changes + * @alias Change + * @param {String} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + function assertChanges (object, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object'); + new Assertion(object, msg).to.have.property(prop); + new Assertion(fn).is.a('function'); + + var initial = object[prop]; + fn(); + + this.assert( + initial !== object[prop] + , 'expected .' + prop + ' to change' + , 'expected .' + prop + ' to not change' + ); + } + + Assertion.addChainableMethod('change', assertChanges); + Assertion.addChainableMethod('changes', assertChanges); + + /** + * ### .increase(function) + * + * Asserts that a function increases an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * expect(fn).to.increase(obj, 'val'); + * + * @name increase + * @alias increases + * @alias Increase + * @param {String} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + function assertIncreases (object, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object'); + new Assertion(object, msg).to.have.property(prop); + new Assertion(fn).is.a('function'); + + var initial = object[prop]; + fn(); + + this.assert( + object[prop] - initial > 0 + , 'expected .' + prop + ' to increase' + , 'expected .' + prop + ' to not increase' + ); + } + + Assertion.addChainableMethod('increase', assertIncreases); + Assertion.addChainableMethod('increases', assertIncreases); + + /** + * ### .decrease(function) + * + * Asserts that a function decreases an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * expect(fn).to.decrease(obj, 'val'); + * + * @name decrease + * @alias decreases + * @alias Decrease + * @param {String} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + function assertDecreases (object, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object'); + new Assertion(object, msg).to.have.property(prop); + new Assertion(fn).is.a('function'); + + var initial = object[prop]; + fn(); + + this.assert( + object[prop] - initial < 0 + , 'expected .' + prop + ' to decrease' + , 'expected .' + prop + ' to not decrease' + ); + } + + Assertion.addChainableMethod('decrease', assertDecreases); + Assertion.addChainableMethod('decreases', assertDecreases); + +}; + +}); + +require.register("chai/lib/chai/interface/assert.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + + +module.exports = function (chai, util) { + + /*! + * Chai dependencies. + */ + + var Assertion = chai.Assertion + , flag = util.flag; + + /*! + * Module export. + */ + + /** + * ### assert(expression, message) + * + * Write your own test expressions. + * + * assert('foo' !== 'bar', 'foo is not bar'); + * assert(Array.isArray([]), 'empty arrays are arrays'); + * + * @param {Mixed} expression to test for truthiness + * @param {String} message to display on error + * @name assert + * @api public + */ + + var assert = chai.assert = function (express, errmsg) { + var test = new Assertion(null, null, chai.assert); + test.assert( + express + , errmsg + , '[ negation message unavailable ]' + ); + }; + + /** + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. Node.js `assert` module-compatible. + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @api public + */ + + assert.fail = function (actual, expected, message, operator) { + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, assert.fail); + }; + + /** + * ### .ok(object, [message]) + * + * Asserts that `object` is truthy. + * + * assert.ok('everything', 'everything is ok'); + * assert.ok(false, 'this will fail'); + * + * @name ok + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.ok = function (val, msg) { + new Assertion(val, msg).is.ok; + }; + + /** + * ### .notOk(object, [message]) + * + * Asserts that `object` is falsy. + * + * assert.notOk('everything', 'this will fail'); + * assert.notOk(false, 'this will pass'); + * + * @name notOk + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.notOk = function (val, msg) { + new Assertion(val, msg).is.not.ok; + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * assert.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.equal = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.equal); + + test.assert( + exp == flag(test, 'object') + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , exp + , act + ); + }; + + /** + * ### .notEqual(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * assert.notEqual(3, 4, 'these numbers are not equal'); + * + * @name notEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notEqual = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.notEqual); + + test.assert( + exp != flag(test, 'object') + , 'expected #{this} to not equal #{exp}' + , 'expected #{this} to equal #{act}' + , exp + , act + ); + }; + + /** + * ### .strictEqual(actual, expected, [message]) + * + * Asserts strict equality (`===`) of `actual` and `expected`. + * + * assert.strictEqual(true, true, 'these booleans are strictly equal'); + * + * @name strictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.strictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.equal(exp); + }; + + /** + * ### .notStrictEqual(actual, expected, [message]) + * + * Asserts strict inequality (`!==`) of `actual` and `expected`. + * + * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); + * + * @name notStrictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notStrictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.equal(exp); + }; + + /** + * ### .deepEqual(actual, expected, [message]) + * + * Asserts that `actual` is deeply equal to `expected`. + * + * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); + * + * @name deepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.deepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.eql(exp); + }; + + /** + * ### .notDeepEqual(actual, expected, [message]) + * + * Assert that `actual` is not deeply equal to `expected`. + * + * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); + * + * @name notDeepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notDeepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.eql(exp); + }; + + /** + * ### .isTrue(value, [message]) + * + * Asserts that `value` is true. + * + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); + * + * @name isTrue + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isAbove = function (val, abv, msg) { + new Assertion(val, msg).to.be.above(abv); + }; + + /** + * ### .isAbove(valueToCheck, valueToBeAbove, [message]) + * + * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove` + * + * assert.isAbove(5, 2, '5 is strictly greater than 2'); + * + * @name isAbove + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAbove + * @param {String} message + * @api public + */ + + assert.isBelow = function (val, blw, msg) { + new Assertion(val, msg).to.be.below(blw); + }; + + /** + * ### .isBelow(valueToCheck, valueToBeBelow, [message]) + * + * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow` + * + * assert.isBelow(3, 6, '3 is strictly less than 6'); + * + * @name isBelow + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeBelow + * @param {String} message + * @api public + */ + + assert.isTrue = function (val, msg) { + new Assertion(val, msg).is['true']; + }; + + /** + * ### .isFalse(value, [message]) + * + * Asserts that `value` is false. + * + * var teaServed = false; + * assert.isFalse(teaServed, 'no tea yet? hmm...'); + * + * @name isFalse + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFalse = function (val, msg) { + new Assertion(val, msg).is['false']; + }; + + /** + * ### .isNull(value, [message]) + * + * Asserts that `value` is null. + * + * assert.isNull(err, 'there was no error'); + * + * @name isNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNull = function (val, msg) { + new Assertion(val, msg).to.equal(null); + }; + + /** + * ### .isNotNull(value, [message]) + * + * Asserts that `value` is not null. + * + * var tea = 'tasty chai'; + * assert.isNotNull(tea, 'great, time for tea!'); + * + * @name isNotNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNull = function (val, msg) { + new Assertion(val, msg).to.not.equal(null); + }; + + /** + * ### .isUndefined(value, [message]) + * + * Asserts that `value` is `undefined`. + * + * var tea; + * assert.isUndefined(tea, 'no tea defined'); + * + * @name isUndefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isUndefined = function (val, msg) { + new Assertion(val, msg).to.equal(undefined); + }; + + /** + * ### .isDefined(value, [message]) + * + * Asserts that `value` is not `undefined`. + * + * var tea = 'cup of chai'; + * assert.isDefined(tea, 'tea has been defined'); + * + * @name isDefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isDefined = function (val, msg) { + new Assertion(val, msg).to.not.equal(undefined); + }; + + /** + * ### .isFunction(value, [message]) + * + * Asserts that `value` is a function. + * + * function serveTea() { return 'cup of tea'; }; + * assert.isFunction(serveTea, 'great, we can have tea now'); + * + * @name isFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFunction = function (val, msg) { + new Assertion(val, msg).to.be.a('function'); + }; + + /** + * ### .isNotFunction(value, [message]) + * + * Asserts that `value` is _not_ a function. + * + * var serveTea = [ 'heat', 'pour', 'sip' ]; + * assert.isNotFunction(serveTea, 'great, we have listed the steps'); + * + * @name isNotFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotFunction = function (val, msg) { + new Assertion(val, msg).to.not.be.a('function'); + }; + + /** + * ### .isObject(value, [message]) + * + * Asserts that `value` is an object (as revealed by + * `Object.prototype.toString`). + * + * var selection = { name: 'Chai', serve: 'with spices' }; + * assert.isObject(selection, 'tea selection is an object'); + * + * @name isObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isObject = function (val, msg) { + new Assertion(val, msg).to.be.a('object'); + }; + + /** + * ### .isNotObject(value, [message]) + * + * Asserts that `value` is _not_ an object. + * + * var selection = 'chai' + * assert.isNotObject(selection, 'tea selection is not an object'); + * assert.isNotObject(null, 'null is not an object'); + * + * @name isNotObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotObject = function (val, msg) { + new Assertion(val, msg).to.not.be.a('object'); + }; + + /** + * ### .isArray(value, [message]) + * + * Asserts that `value` is an array. + * + * var menu = [ 'green', 'chai', 'oolong' ]; + * assert.isArray(menu, 'what kind of tea do we want?'); + * + * @name isArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isArray = function (val, msg) { + new Assertion(val, msg).to.be.an('array'); + }; + + /** + * ### .isNotArray(value, [message]) + * + * Asserts that `value` is _not_ an array. + * + * var menu = 'green|chai|oolong'; + * assert.isNotArray(menu, 'what kind of tea do we want?'); + * + * @name isNotArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotArray = function (val, msg) { + new Assertion(val, msg).to.not.be.an('array'); + }; + + /** + * ### .isString(value, [message]) + * + * Asserts that `value` is a string. + * + * var teaOrder = 'chai'; + * assert.isString(teaOrder, 'order placed'); + * + * @name isString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isString = function (val, msg) { + new Assertion(val, msg).to.be.a('string'); + }; + + /** + * ### .isNotString(value, [message]) + * + * Asserts that `value` is _not_ a string. + * + * var teaOrder = 4; + * assert.isNotString(teaOrder, 'order placed'); + * + * @name isNotString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotString = function (val, msg) { + new Assertion(val, msg).to.not.be.a('string'); + }; + + /** + * ### .isNumber(value, [message]) + * + * Asserts that `value` is a number. + * + * var cups = 2; + * assert.isNumber(cups, 'how many cups'); + * + * @name isNumber + * @param {Number} value + * @param {String} message + * @api public + */ + + assert.isNumber = function (val, msg) { + new Assertion(val, msg).to.be.a('number'); + }; + + /** + * ### .isNotNumber(value, [message]) + * + * Asserts that `value` is _not_ a number. + * + * var cups = '2 cups please'; + * assert.isNotNumber(cups, 'how many cups'); + * + * @name isNotNumber + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNumber = function (val, msg) { + new Assertion(val, msg).to.not.be.a('number'); + }; + + /** + * ### .isBoolean(value, [message]) + * + * Asserts that `value` is a boolean. + * + * var teaReady = true + * , teaServed = false; + * + * assert.isBoolean(teaReady, 'is the tea ready'); + * assert.isBoolean(teaServed, 'has tea been served'); + * + * @name isBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isBoolean = function (val, msg) { + new Assertion(val, msg).to.be.a('boolean'); + }; + + /** + * ### .isNotBoolean(value, [message]) + * + * Asserts that `value` is _not_ a boolean. + * + * var teaReady = 'yep' + * , teaServed = 'nope'; + * + * assert.isNotBoolean(teaReady, 'is the tea ready'); + * assert.isNotBoolean(teaServed, 'has tea been served'); + * + * @name isNotBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotBoolean = function (val, msg) { + new Assertion(val, msg).to.not.be.a('boolean'); + }; + + /** + * ### .typeOf(value, name, [message]) + * + * Asserts that `value`'s type is `name`, as determined by + * `Object.prototype.toString`. + * + * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); + * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); + * assert.typeOf('tea', 'string', 'we have a string'); + * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); + * assert.typeOf(null, 'null', 'we have a null'); + * assert.typeOf(undefined, 'undefined', 'we have an undefined'); + * + * @name typeOf + * @param {Mixed} value + * @param {String} name + * @param {String} message + * @api public + */ + + assert.typeOf = function (val, type, msg) { + new Assertion(val, msg).to.be.a(type); + }; + + /** + * ### .notTypeOf(value, name, [message]) + * + * Asserts that `value`'s type is _not_ `name`, as determined by + * `Object.prototype.toString`. + * + * assert.notTypeOf('tea', 'number', 'strings are not numbers'); + * + * @name notTypeOf + * @param {Mixed} value + * @param {String} typeof name + * @param {String} message + * @api public + */ + + assert.notTypeOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.a(type); + }; + + /** + * ### .instanceOf(object, constructor, [message]) + * + * Asserts that `value` is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new Tea('chai'); + * + * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); + * + * @name instanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.instanceOf = function (val, type, msg) { + new Assertion(val, msg).to.be.instanceOf(type); + }; + + /** + * ### .notInstanceOf(object, constructor, [message]) + * + * Asserts `value` is not an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new String('chai'); + * + * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); + * + * @name notInstanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.notInstanceOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.instanceOf(type); + }; + + /** + * ### .include(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Works + * for strings and arrays. + * + * assert.include('foobar', 'bar', 'foobar contains string "bar"'); + * assert.include([ 1, 2, 3 ], 3, 'array contains value'); + * + * @name include + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.include = function (exp, inc, msg) { + new Assertion(exp, msg, assert.include).include(inc); + }; + + /** + * ### .notInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Works + * for strings and arrays. + *i + * assert.notInclude('foobar', 'baz', 'string not include substring'); + * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); + * + * @name notInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.notInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notInclude).not.include(inc); + }; + + /** + * ### .match(value, regexp, [message]) + * + * Asserts that `value` matches the regular expression `regexp`. + * + * assert.match('foobar', /^foo/, 'regexp matches'); + * + * @name match + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.match = function (exp, re, msg) { + new Assertion(exp, msg).to.match(re); + }; + + /** + * ### .notMatch(value, regexp, [message]) + * + * Asserts that `value` does not match the regular expression `regexp`. + * + * assert.notMatch('foobar', /^foo/, 'regexp does not match'); + * + * @name notMatch + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.notMatch = function (exp, re, msg) { + new Assertion(exp, msg).to.not.match(re); + }; + + /** + * ### .property(object, property, [message]) + * + * Asserts that `object` has a property named by `property`. + * + * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * + * @name property + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.property = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.property(prop); + }; + + /** + * ### .notProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`. + * + * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.property(prop); + }; + + /** + * ### .deepProperty(object, property, [message]) + * + * Asserts that `object` has a property named by `property`, which can be a + * string using dot- and bracket-notation for deep reference. + * + * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name deepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.deepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.deep.property(prop); + }; + + /** + * ### .notDeepProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for deep reference. + * + * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); + * + * @name notDeepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notDeepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop); + }; + + /** + * ### .propertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. + * + * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); + * + * @name propertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.property(prop, val); + }; + + /** + * ### .propertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. + * + * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); + * + * @name propertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.property(prop, val); + }; + + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for deep + * reference. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.deep.property(prop, val); + }; + + /** + * ### .deepPropertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. `property` can use dot- and + * bracket-notation for deep reference. + * + * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * + * @name deepPropertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop, val); + }; + + /** + * ### .lengthOf(object, length, [message]) + * + * Asserts that `object` has a `length` property with the expected value. + * + * assert.lengthOf([1,2,3], 3, 'array has length of 3'); + * assert.lengthOf('foobar', 5, 'string has length of 6'); + * + * @name lengthOf + * @param {Mixed} object + * @param {Number} length + * @param {String} message + * @api public + */ + + assert.lengthOf = function (exp, len, msg) { + new Assertion(exp, msg).to.have.length(len); + }; + + /** + * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) + * + * Asserts that `function` will throw an error that is an instance of + * `constructor`, or alternately that it will throw an error with message + * matching `regexp`. + * + * assert.throw(fn, 'function throws a reference error'); + * assert.throw(fn, /function throws a reference error/); + * assert.throw(fn, ReferenceError); + * assert.throw(fn, ReferenceError, 'function throws a reference error'); + * assert.throw(fn, ReferenceError, /function throws a reference error/); + * + * @name throws + * @alias throw + * @alias Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.Throw = function (fn, errt, errs, msg) { + if ('string' === typeof errt || errt instanceof RegExp) { + errs = errt; + errt = null; + } + + var assertErr = new Assertion(fn, msg).to.Throw(errt, errs); + return flag(assertErr, 'object'); + }; + + /** + * ### .doesNotThrow(function, [constructor/regexp], [message]) + * + * Asserts that `function` will _not_ throw an error that is an instance of + * `constructor`, or alternately that it will not throw an error with message + * matching `regexp`. + * + * assert.doesNotThrow(fn, Error, 'function does not throw'); + * + * @name doesNotThrow + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.doesNotThrow = function (fn, type, msg) { + if ('string' === typeof type) { + msg = type; + type = null; + } + + new Assertion(fn, msg).to.not.Throw(type); + }; + + /** + * ### .operator(val1, operator, val2, [message]) + * + * Compares two values using `operator`. + * + * assert.operator(1, '<', 2, 'everything is ok'); + * assert.operator(1, '>', 2, 'this will fail'); + * + * @name operator + * @param {Mixed} val1 + * @param {String} operator + * @param {Mixed} val2 + * @param {String} message + * @api public + */ + + assert.operator = function (val, operator, val2, msg) { + if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { + throw new Error('Invalid operator "' + operator + '"'); + } + var test = new Assertion(eval(val + operator + val2), msg); + test.assert( + true === flag(test, 'object') + , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) + , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); + }; + + /** + * ### .closeTo(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); + * + * @name closeTo + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @api public + */ + + assert.closeTo = function (act, exp, delta, msg) { + new Assertion(act, msg).to.be.closeTo(exp, delta); + }; + + /** + * ### .sameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members. + * Order is not taken into account. + * + * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); + * + * @name sameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @api public + */ + + assert.sameMembers = function (set1, set2, msg) { + new Assertion(set1, msg).to.have.same.members(set2); + } + + /** + * ### .sameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members - using a deep equality checking. + * Order is not taken into account. + * + * assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members'); + * + * @name sameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @api public + */ + + assert.sameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg).to.have.same.deep.members(set2); + } + + /** + * ### .includeMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset`. + * Order is not taken into account. + * + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); + * + * @name includeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @api public + */ + + assert.includeMembers = function (superset, subset, msg) { + new Assertion(superset, msg).to.include.members(subset); + } + + /** + * ### .changes(function, object, property) + * + * Asserts that a function changes the value of a property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 22 }; + * assert.changes(fn, obj, 'val'); + * + * @name changes + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.changes = function (fn, obj, prop) { + new Assertion(fn).to.change(obj, prop); + } + + /** + * ### .doesNotChange(function, object, property) + * + * Asserts that a function does not changes the value of a property + * + * var obj = { val: 10 }; + * var fn = function() { console.log('foo'); }; + * assert.doesNotChange(fn, obj, 'val'); + * + * @name doesNotChange + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.doesNotChange = function (fn, obj, prop) { + new Assertion(fn).to.not.change(obj, prop); + } + + /** + * ### .increases(function, object, property) + * + * Asserts that a function increases an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 13 }; + * assert.increases(fn, obj, 'val'); + * + * @name increases + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.increases = function (fn, obj, prop) { + new Assertion(fn).to.increase(obj, prop); + } + + /** + * ### .doesNotIncrease(function, object, property) + * + * Asserts that a function does not increase object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 8 }; + * assert.doesNotIncrease(fn, obj, 'val'); + * + * @name doesNotIncrease + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.doesNotIncrease = function (fn, obj, prop) { + new Assertion(fn).to.not.increase(obj, prop); + } + + /** + * ### .decreases(function, object, property) + * + * Asserts that a function decreases an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreases(fn, obj, 'val'); + * + * @name decreases + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.decreases = function (fn, obj, prop) { + new Assertion(fn).to.decrease(obj, prop); + } + + /** + * ### .doesNotDecrease(function, object, property) + * + * Asserts that a function does not decreases an object property + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.doesNotDecrease(fn, obj, 'val'); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object + * @param {String} property name + * @param {String} message _optional_ + * @api public + */ + + assert.doesNotDecrease = function (fn, obj, prop) { + new Assertion(fn).to.not.decrease(obj, prop); + } + + /*! + * Undocumented / untested + */ + + assert.ifError = function (val, msg) { + new Assertion(val, msg).to.not.be.ok; + }; + + /*! + * Aliases. + */ + + (function alias(name, as){ + assert[as] = assert[name]; + return alias; + }) + ('Throw', 'throw') + ('Throw', 'throws'); +}; + +}); + +require.register("chai/lib/chai/interface/expect.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + chai.expect = function (val, message) { + return new chai.Assertion(val, message); + }; + + /** + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @api public + */ + + chai.expect.fail = function (actual, expected, message, operator) { + message = message || 'expect.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, chai.expect.fail); + }; +}; + +}); + +require.register("chai/lib/chai/interface/should.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + var Assertion = chai.Assertion; + + function loadShould () { + // explicitly define this method as function as to have it's name to include as `ssfi` + function shouldGetter() { + if (this instanceof String || this instanceof Number) { + return new Assertion(this.constructor(this), null, shouldGetter); + } else if (this instanceof Boolean) { + return new Assertion(this == true, null, shouldGetter); + } + return new Assertion(this, null, shouldGetter); + } + function shouldSetter(value) { + // See https://github.com/chaijs/chai/issues/86: this makes + // `whatever.should = someValue` actually set `someValue`, which is + // especially useful for `global.should = require('chai').should()`. + // + // Note that we have to use [[DefineProperty]] instead of [[Put]] + // since otherwise we would trigger this very setter! + Object.defineProperty(this, 'should', { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } + // modify Object.prototype to have `should` + Object.defineProperty(Object.prototype, 'should', { + set: shouldSetter + , get: shouldGetter + , configurable: true + }); + + var should = {}; + + /** + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @api public + */ + + should.fail = function (actual, expected, message, operator) { + message = message || 'should.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, should.fail); + }; + + should.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.equal(val2); + }; + + should.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.Throw(errt, errs); + }; + + should.exist = function (val, msg) { + new Assertion(val, msg).to.exist; + } + + // negation + should.not = {} + + should.not.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.not.equal(val2); + }; + + should.not.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.not.Throw(errt, errs); + }; + + should.not.exist = function (val, msg) { + new Assertion(val, msg).to.not.exist; + } + + should['throw'] = should['Throw']; + should.not['throw'] = should.not['Throw']; + + return should; + }; + + chai.should = loadShould; + chai.Should = loadShould; +}; + +}); + +require.register("chai/lib/chai/utils/addChainableMethod.js", function (exports, module) { +/*! + * Chai - addChainingMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var transferFlags = require('chai/lib/chai/utils/transferFlags.js'); +var flag = require('chai/lib/chai/utils/flag.js'); +var config = require('chai/lib/chai/config.js'); + +/*! + * Module variables + */ + +// Check whether `__proto__` is supported +var hasProtoSupport = '__proto__' in Object; + +// Without `__proto__` support, this module will need to add properties to a function. +// However, some Function.prototype methods cannot be overwritten, +// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). +var excludeNames = /^(?:length|name|arguments|caller)$/; + +// Cache `Function` properties +var call = Function.prototype.call, + apply = Function.prototype.apply; + +/** + * ### addChainableMethod (ctx, name, method, chainingBehavior) + * + * Adds a method to an object, such that the method can also be chained. + * + * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); + * + * The result can then be used as both a method assertion, executing both `method` and + * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. + * + * expect(fooStr).to.be.foo('bar'); + * expect(fooStr).to.be.foo.equal('foo'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for `name`, when called + * @param {Function} chainingBehavior function to be called every time the property is accessed + * @name addChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + if (typeof chainingBehavior !== 'function') { + chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; + + Object.defineProperty(ctx, name, + { get: function () { + chainableBehavior.chainingBehavior.call(this); + + var assert = function assert() { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', assert); + var result = chainableBehavior.method.apply(this, arguments); + return result === undefined ? this : result; + }; + + // Use `__proto__` if available + if (hasProtoSupport) { + // Inherit all properties from the object by replacing the `Function` prototype + var prototype = assert.__proto__ = Object.create(this); + // Restore the `call` and `apply` methods from `Function` + prototype.call = call; + prototype.apply = apply; + } + // Otherwise, redefine all properties (slow!) + else { + var asserterNames = Object.getOwnPropertyNames(ctx); + asserterNames.forEach(function (asserterName) { + if (!excludeNames.test(asserterName)) { + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(assert, asserterName, pd); + } + }); + } + + transferFlags(this, assert); + return assert; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/addMethod.js", function (exports, module) { +/*! + * Chai - addMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var config = require('chai/lib/chai/config.js'); + +/** + * ### .addMethod (ctx, name, method) + * + * Adds a method to the prototype of an object. + * + * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(fooStr).to.be.foo('bar'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for name + * @name addMethod + * @api public + */ +var flag = require('chai/lib/chai/utils/flag.js'); + +module.exports = function (ctx, name, method) { + ctx[name] = function () { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', ctx[name]); + var result = method.apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); + +require.register("chai/lib/chai/utils/addProperty.js", function (exports, module) { +/*! + * Chai - addProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### addProperty (ctx, name, getter) + * + * Adds a property to the prototype of an object. + * + * utils.addProperty(chai.Assertion.prototype, 'foo', function () { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.instanceof(Foo); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.foo; + * + * @param {Object} ctx object to which the property is added + * @param {String} name of property to add + * @param {Function} getter function to be used for name + * @name addProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + Object.defineProperty(ctx, name, + { get: function () { + var result = getter.call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/flag.js", function (exports, module) { +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### flag(object, key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {Object} object constructed Assertion + * @param {String} key + * @param {Mixed} value (optional) + * @name flag + * @api private + */ + +module.exports = function (obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; + +}); + +require.register("chai/lib/chai/utils/getActual.js", function (exports, module) { +/*! + * Chai - getActual utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getActual(object, [actual]) + * + * Returns the `actual` value for an Assertion + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + return args.length > 4 ? args[4] : obj._obj; +}; + +}); + +require.register("chai/lib/chai/utils/getEnumerableProperties.js", function (exports, module) { +/*! + * Chai - getEnumerableProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getEnumerableProperties(object) + * + * This allows the retrieval of enumerable property names of an object, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getEnumerableProperties + * @api public + */ + +module.exports = function getEnumerableProperties(object) { + var result = []; + for (var name in object) { + result.push(name); + } + return result; +}; + +}); + +require.register("chai/lib/chai/utils/getMessage.js", function (exports, module) { +/*! + * Chai - message composition utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('chai/lib/chai/utils/flag.js') + , getActual = require('chai/lib/chai/utils/getActual.js') + , inspect = require('chai/lib/chai/utils/inspect.js') + , objDisplay = require('chai/lib/chai/utils/objDisplay.js'); + +/** + * ### .getMessage(object, message, negateMessage) + * + * Construct the error message based on flags + * and template tags. Template tags will return + * a stringified inspection of the object referenced. + * + * Message template tags: + * - `#{this}` current asserted object + * - `#{act}` actual value + * - `#{exp}` expected value + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @name getMessage + * @api public + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , val = flag(obj, 'object') + , expected = args[3] + , actual = getActual(obj, args) + , msg = negate ? args[2] : args[1] + , flagMsg = flag(obj, 'message'); + + if(typeof msg === "function") msg = msg(); + msg = msg || ''; + msg = msg + .replace(/#{this}/g, objDisplay(val)) + .replace(/#{act}/g, objDisplay(actual)) + .replace(/#{exp}/g, objDisplay(expected)); + + return flagMsg ? flagMsg + ': ' + msg : msg; +}; + +}); + +require.register("chai/lib/chai/utils/getName.js", function (exports, module) { +/*! + * Chai - getName utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getName(func) + * + * Gets the name of a function, in a cross-browser way. + * + * @param {Function} a function (usually a constructor) + */ + +module.exports = function (func) { + if (func.name) return func.name; + + var match = /^\s?function ([^(]*)\(/.exec(func); + return match && match[1] ? match[1] : ""; +}; + +}); + +require.register("chai/lib/chai/utils/getPathValue.js", function (exports, module) { +/*! + * Chai - getPathValue utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +var getPathInfo = require('chai/lib/chai/utils/getPathInfo.js'); + +/** + * ### .getPathValue(path, object) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue('prop1.str', obj); // Hello + * getPathValue('prop1.att[2]', obj); // b + * getPathValue('prop2.arr[0].nested', obj); // Universe + * + * @param {String} path + * @param {Object} object + * @returns {Object} value or `undefined` + * @name getPathValue + * @api public + */ +module.exports = function(path, obj) { + var info = getPathInfo(path, obj); + return info.value; +}; + +}); + +require.register("chai/lib/chai/utils/getPathInfo.js", function (exports, module) { +/*! + * Chai - getPathInfo utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var hasProperty = require('chai/lib/chai/utils/hasProperty.js'); + +/** + * ### .getPathInfo(path, object) + * + * This allows the retrieval of property info in an + * object given a string path. + * + * The path info consists of an object with the + * following properties: + * + * * parent - The parent object of the property referenced by `path` + * * name - The name of the final property, a number if it was an array indexer + * * value - The value of the property, if it exists, otherwise `undefined` + * * exists - Whether the property exists or not + * + * @param {String} path + * @param {Object} object + * @returns {Object} info + * @name getPathInfo + * @api public + */ + +module.exports = function getPathInfo(path, obj) { + var parsed = parsePath(path), + last = parsed[parsed.length - 1]; + + var info = { + parent: _getPathValue(parsed, obj, parsed.length - 1), + name: last.p || last.i, + value: _getPathValue(parsed, obj), + }; + info.exists = hasProperty(info.name, info.parent); + + return info; +}; + + +/*! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `_getPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be as near infinitely deep and nested + * * Arrays are also valid using the formal `myobject.document[3].property`. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath (path) { + var str = path.replace(/\[/g, '.[') + , parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function (value) { + var re = /\[(\d+)\]$/ + , mArr = re.exec(value); + if (mArr) return { i: parseFloat(mArr[1]) }; + else return { p: value }; + }); +} + + +/*! + * ## _getPathValue(parsed, obj) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(parsed, obj); + * + * @param {Object} parsed definition from `parsePath`. + * @param {Object} object to search against + * @param {Number} object to search against + * @returns {Object|Undefined} value + * @api private + */ + +function _getPathValue (parsed, obj, index) { + var tmp = obj + , res; + + index = (index === undefined ? parsed.length : index); + + for (var i = 0, l = index; i < l; i++) { + var part = parsed[i]; + if (tmp) { + if ('undefined' !== typeof part.p) + tmp = tmp[part.p]; + else if ('undefined' !== typeof part.i) + tmp = tmp[part.i]; + if (i == (l - 1)) res = tmp; + } else { + res = undefined; + } + } + return res; +} + +}); + +require.register("chai/lib/chai/utils/hasProperty.js", function (exports, module) { +/*! + * Chai - hasProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var type = require('chai/lib/chai/utils/type.js'); + +/** + * ### .hasProperty(object, name) + * + * This allows checking whether an object has + * named property or numeric array index. + * + * Basically does the same thing as the `in` + * operator but works properly with natives + * and null/undefined values. + * + * var obj = { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * + * The following would be the results. + * + * hasProperty('str', obj); // true + * hasProperty('constructor', obj); // true + * hasProperty('bar', obj); // false + * + * hasProperty('length', obj.str); // true + * hasProperty(1, obj.str); // true + * hasProperty(5, obj.str); // false + * + * hasProperty('length', obj.arr); // true + * hasProperty(2, obj.arr); // true + * hasProperty(3, obj.arr); // false + * + * @param {Objuect} object + * @param {String|Number} name + * @returns {Boolean} whether it exists + * @name getPathInfo + * @api public + */ + +var literals = { + 'number': Number + , 'string': String +}; + +module.exports = function hasProperty(name, obj) { + var ot = type(obj); + + // Bad Object, obviously no props at all + if(ot === 'null' || ot === 'undefined') + return false; + + // The `in` operator does not work with certain literals + // box these before the check + if(literals[ot] && typeof obj !== 'object') + obj = new literals[ot](obj); + + return name in obj; +}; + +}); + +require.register("chai/lib/chai/utils/getProperties.js", function (exports, module) { +/*! + * Chai - getProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getProperties(object) + * + * This allows the retrieval of property names of an object, enumerable or not, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getProperties + * @api public + */ + +module.exports = function getProperties(object) { + var result = Object.getOwnPropertyNames(subject); + + function addProperty(property) { + if (result.indexOf(property) === -1) { + result.push(property); + } + } + + var proto = Object.getPrototypeOf(subject); + while (proto !== null) { + Object.getOwnPropertyNames(proto).forEach(addProperty); + proto = Object.getPrototypeOf(proto); + } + + return result; +}; + +}); + +require.register("chai/lib/chai/utils/index.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011 Jake Luer + * MIT Licensed + */ + +/*! + * Main exports + */ + +var exports = module.exports = {}; + +/*! + * test utility + */ + +exports.test = require('chai/lib/chai/utils/test.js'); + +/*! + * type utility + */ + +exports.type = require('chai/lib/chai/utils/type.js'); + +/*! + * message utility + */ + +exports.getMessage = require('chai/lib/chai/utils/getMessage.js'); + +/*! + * actual utility + */ + +exports.getActual = require('chai/lib/chai/utils/getActual.js'); + +/*! + * Inspect util + */ + +exports.inspect = require('chai/lib/chai/utils/inspect.js'); + +/*! + * Object Display util + */ + +exports.objDisplay = require('chai/lib/chai/utils/objDisplay.js'); + +/*! + * Flag utility + */ + +exports.flag = require('chai/lib/chai/utils/flag.js'); + +/*! + * Flag transferring utility + */ + +exports.transferFlags = require('chai/lib/chai/utils/transferFlags.js'); + +/*! + * Deep equal utility + */ + +exports.eql = require('chaijs~deep-eql@0.1.3'); + +/*! + * Deep path value + */ + +exports.getPathValue = require('chai/lib/chai/utils/getPathValue.js'); + +/*! + * Deep path info + */ + +exports.getPathInfo = require('chai/lib/chai/utils/getPathInfo.js'); + +/*! + * Check if a property exists + */ + +exports.hasProperty = require('chai/lib/chai/utils/hasProperty.js'); + +/*! + * Function name + */ + +exports.getName = require('chai/lib/chai/utils/getName.js'); + +/*! + * add Property + */ + +exports.addProperty = require('chai/lib/chai/utils/addProperty.js'); + +/*! + * add Method + */ + +exports.addMethod = require('chai/lib/chai/utils/addMethod.js'); + +/*! + * overwrite Property + */ + +exports.overwriteProperty = require('chai/lib/chai/utils/overwriteProperty.js'); + +/*! + * overwrite Method + */ + +exports.overwriteMethod = require('chai/lib/chai/utils/overwriteMethod.js'); + +/*! + * Add a chainable method + */ + +exports.addChainableMethod = require('chai/lib/chai/utils/addChainableMethod.js'); + +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('chai/lib/chai/utils/overwriteChainableMethod.js'); + + +}); + +require.register("chai/lib/chai/utils/inspect.js", function (exports, module) { +// This is (almost) directly from Node.js utils +// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js + +var getName = require('chai/lib/chai/utils/getName.js'); +var getProperties = require('chai/lib/chai/utils/getProperties.js'); +var getEnumerableProperties = require('chai/lib/chai/utils/getEnumerableProperties.js'); + +module.exports = inspect; + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Boolean} showHidden Flag that shows hidden (not enumerable) + * properties of objects. + * @param {Number} depth Depth in which to descend in object. Default is 2. + * @param {Boolean} colors Flag to turn on ANSI escape codes to color the + * output. Default is false (no coloring). + */ +function inspect(obj, showHidden, depth, colors) { + var ctx = { + showHidden: showHidden, + seen: [], + stylize: function (str) { return str; } + }; + return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); +} + +// Returns true if object is a DOM element. +var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } +}; + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes); + if (typeof ret !== 'string') { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // If this is a DOM element, try to get the outer HTML. + if (isDOMElement(value)) { + if ('outerHTML' in value) { + return value.outerHTML; + // This value does not have an outerHTML attribute, + // it could still be an XML element + } else { + // Attempt to serialize it + try { + if (document.xmlVersion) { + var xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(value); + } else { + // Firefox 11- do not support outerHTML + // It does, however, support innerHTML + // Use the following to render the element + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + + container.appendChild(value.cloneNode(false)); + html = container.innerHTML + .replace('><', '>' + value.innerHTML + '<'); + container.innerHTML = ''; + return html; + } + } catch (err) { + // This could be a non-native DOM implementation, + // continue with the normal flow: + // printing the element as if it is an object. + } + } + } + + // Look up the keys of the object. + var visibleKeys = getEnumerableProperties(value); + var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + + // Some type of object without properties can be shortcutted. + // In IE, errors have a single `stack` property, or if they are vanilla `Error`, + // a `stack` plus `description` property; ignore those for consistency. + if (keys.length === 0 || (isError(value) && ( + (keys.length === 1 && keys[0] === 'stack') || + (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') + ))) { + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + return ctx.stylize('[Function' + nameSuffix + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + base = ' [Function' + nameSuffix + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + return formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + if (value === 0 && (1/value) === -Infinity) { + return ctx.stylize('-0', 'number'); + } + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str; + if (value.__lookupGetter__) { + if (value.__lookupGetter__(key)) { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Setter]', 'special'); + } + } + } + if (visibleKeys.indexOf(key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(value[key]) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, value[key], null); + } else { + str = formatValue(ctx, value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +}); + +require.register("chai/lib/chai/utils/objDisplay.js", function (exports, module) { +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var inspect = require('chai/lib/chai/utils/inspect.js'); +var config = require('chai/lib/chai/config.js'); + +/** + * ### .objDisplay (object) + * + * Determines if an object or an array matches + * criteria to be inspected in-line for error + * messages or should be truncated. + * + * @param {Mixed} javascript object to inspect + * @name objDisplay + * @api public + */ + +module.exports = function (obj) { + var str = inspect(obj) + , type = Object.prototype.toString.call(obj); + + if (config.truncateThreshold && str.length >= config.truncateThreshold) { + if (type === '[object Function]') { + return !obj.name || obj.name === '' + ? '[Function]' + : '[Function: ' + obj.name + ']'; + } else if (type === '[object Array]') { + return '[ Array(' + obj.length + ') ]'; + } else if (type === '[object Object]') { + var keys = Object.keys(obj) + , kstr = keys.length > 2 + ? keys.splice(0, 2).join(', ') + ', ...' + : keys.join(', '); + return '{ Object (' + kstr + ') }'; + } else { + return str; + } + } else { + return str; + } +}; + +}); + +require.register("chai/lib/chai/utils/overwriteMethod.js", function (exports, module) { +/*! + * Chai - overwriteMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteMethod (ctx, name, fn) + * + * Overwites an already existing method and provides + * access to previous function. Must return function + * to be used for name. + * + * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { + * return function (str) { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.value).to.equal(str); + * } else { + * _super.apply(this, arguments); + * } + * } + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.equal('bar'); + * + * @param {Object} ctx object whose method is to be overwritten + * @param {String} name of method to overwrite + * @param {Function} method function that returns a function to be used for name + * @name overwriteMethod + * @api public + */ + +module.exports = function (ctx, name, method) { + var _method = ctx[name] + , _super = function () { return this; }; + + if (_method && 'function' === typeof _method) + _super = _method; + + ctx[name] = function () { + var result = method(_super).apply(this, arguments); + return result === undefined ? this : result; + } +}; + +}); + +require.register("chai/lib/chai/utils/overwriteProperty.js", function (exports, module) { +/*! + * Chai - overwriteProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteProperty (ctx, name, fn) + * + * Overwites an already existing property getter and provides + * access to previous value. Must return function to use as getter. + * + * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { + * return function () { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.name).to.equal('bar'); + * } else { + * _super.call(this); + * } + * } + * }); + * + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.ok; + * + * @param {Object} ctx object whose property is to be overwritten + * @param {String} name of property to overwrite + * @param {Function} getter function that returns a getter function to be used for name + * @name overwriteProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + var _get = Object.getOwnPropertyDescriptor(ctx, name) + , _super = function () {}; + + if (_get && 'function' === typeof _get.get) + _super = _get.get + + Object.defineProperty(ctx, name, + { get: function () { + var result = getter(_super).call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function (exports, module) { +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteChainableMethod (ctx, name, method, chainingBehavior) + * + * Overwites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.length(3); + * expect(myFoo).to.have.length.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function () { + var result = chainingBehavior(_chainingBehavior).call(this); + return result === undefined ? this : result; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function () { + var result = method(_method).apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); + +require.register("chai/lib/chai/utils/test.js", function (exports, module) { +/*! + * Chai - test utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('chai/lib/chai/utils/flag.js'); + +/** + * # test(object, expression) + * + * Test and object for expression. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , expr = args[0]; + return negate ? !expr : expr; +}; + +}); + +require.register("chai/lib/chai/utils/transferFlags.js", function (exports, module) { +/*! + * Chai - transferFlags utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### transferFlags(assertion, object, includeAll = true) + * + * Transfer all the flags for `assertion` to `object`. If + * `includeAll` is set to `false`, then the base Chai + * assertion flags (namely `object`, `ssfi`, and `message`) + * will not be transferred. + * + * + * var newAssertion = new Assertion(); + * utils.transferFlags(assertion, newAssertion); + * + * var anotherAsseriton = new Assertion(myObj); + * utils.transferFlags(assertion, anotherAssertion, false); + * + * @param {Assertion} assertion the assertion to transfer the flags from + * @param {Object} object the object to transfer the flags to; usually a new assertion + * @param {Boolean} includeAll + * @name transferFlags + * @api private + */ + +module.exports = function (assertion, object, includeAll) { + var flags = assertion.__flags || (assertion.__flags = Object.create(null)); + + if (!object.__flags) { + object.__flags = Object.create(null); + } + + includeAll = arguments.length === 3 ? includeAll : true; + + for (var flag in flags) { + if (includeAll || + (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { + object.__flags[flag] = flags[flag]; + } + } +}; + +}); + +require.register("chai/lib/chai/utils/type.js", function (exports, module) { +/*! + * Chai - type utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Arguments]': 'arguments' + , '[object Array]': 'array' + , '[object Date]': 'date' + , '[object Function]': 'function' + , '[object Number]': 'number' + , '[object RegExp]': 'regexp' + , '[object String]': 'string' +}; + +/** + * ### type(object) + * + * Better implementation of `typeof` detection that can + * be used cross-browser. Handles the inconsistencies of + * Array, `null`, and `undefined` detection. + * + * utils.type({}) // 'object' + * utils.type(null) // `null' + * utils.type(undefined) // `undefined` + * utils.type([]) // `array` + * + * @param {Mixed} object to detect type of + * @name type + * @api private + */ + +module.exports = function (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +}; + +}); + +if (typeof exports == "object") { + module.exports = require("chai"); +} else if (typeof define == "function" && define.amd) { + define("chai", [], function(){ return require("chai"); }); +} else { + (this || window)["chai"] = require("chai"); +} +})() diff --git a/node_modules/jaro-winkler/test/vendor/mocha.css b/node_modules/jaro-winkler/test/vendor/mocha.css new file mode 100644 index 0000000..42b9798 --- /dev/null +++ b/node_modules/jaro-winkler/test/vendor/mocha.css @@ -0,0 +1,270 @@ +@charset "utf-8"; + +body { + margin:0; +} + +#mocha { + font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 60px 50px; +} + +#mocha ul, +#mocha li { + margin: 0; + padding: 0; +} + +#mocha ul { + list-style: none; +} + +#mocha h1, +#mocha h2 { + margin: 0; +} + +#mocha h1 { + margin-top: 15px; + font-size: 1em; + font-weight: 200; +} + +#mocha h1 a { + text-decoration: none; + color: inherit; +} + +#mocha h1 a:hover { + text-decoration: underline; +} + +#mocha .suite .suite h1 { + margin-top: 0; + font-size: .8em; +} + +#mocha .hidden { + display: none; +} + +#mocha h2 { + font-size: 12px; + font-weight: normal; + cursor: pointer; +} + +#mocha .suite { + margin-left: 15px; +} + +#mocha .test { + margin-left: 15px; + overflow: hidden; +} + +#mocha .test.pending:hover h2::after { + content: '(pending)'; + font-family: arial, sans-serif; +} + +#mocha .test.pass.medium .duration { + background: #c09853; +} + +#mocha .test.pass.slow .duration { + background: #b94a48; +} + +#mocha .test.pass::before { + content: '✓'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #00d6b2; +} + +#mocha .test.pass .duration { + font-size: 9px; + margin-left: 5px; + padding: 2px 5px; + color: #fff; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + box-shadow: inset 0 1px 1px rgba(0,0,0,.2); + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; +} + +#mocha .test.pass.fast .duration { + display: none; +} + +#mocha .test.pending { + color: #0b97c4; +} + +#mocha .test.pending::before { + content: '◦'; + color: #0b97c4; +} + +#mocha .test.fail { + color: #c00; +} + +#mocha .test.fail pre { + color: black; +} + +#mocha .test.fail::before { + content: '✖'; + font-size: 12px; + display: block; + float: left; + margin-right: 5px; + color: #c00; +} + +#mocha .test pre.error { + color: #c00; + max-height: 300px; + overflow: auto; +} + +/** + * (1): approximate for browsers not supporting calc + * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) + * ^^ seriously + */ +#mocha .test pre { + display: block; + float: left; + clear: left; + font: 12px/1.5 monaco, monospace; + margin: 5px; + padding: 15px; + border: 1px solid #eee; + max-width: 85%; /*(1)*/ + max-width: calc(100% - 42px); /*(2)*/ + word-wrap: break-word; + border-bottom-color: #ddd; + -webkit-border-radius: 3px; + -webkit-box-shadow: 0 1px 3px #eee; + -moz-border-radius: 3px; + -moz-box-shadow: 0 1px 3px #eee; + border-radius: 3px; +} + +#mocha .test h2 { + position: relative; +} + +#mocha .test a.replay { + position: absolute; + top: 3px; + right: 0; + text-decoration: none; + vertical-align: middle; + display: block; + width: 15px; + height: 15px; + line-height: 15px; + text-align: center; + background: #eee; + font-size: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + -webkit-transition: opacity 200ms; + -moz-transition: opacity 200ms; + transition: opacity 200ms; + opacity: 0.3; + color: #888; +} + +#mocha .test:hover a.replay { + opacity: 1; +} + +#mocha-report.pass .test.fail { + display: none; +} + +#mocha-report.fail .test.pass { + display: none; +} + +#mocha-report.pending .test.pass, +#mocha-report.pending .test.fail { + display: none; +} +#mocha-report.pending .test.pass.pending { + display: block; +} + +#mocha-error { + color: #c00; + font-size: 1.5em; + font-weight: 100; + letter-spacing: 1px; +} + +#mocha-stats { + position: fixed; + top: 15px; + right: 10px; + font-size: 12px; + margin: 0; + color: #888; + z-index: 1; +} + +#mocha-stats .progress { + float: right; + padding-top: 0; +} + +#mocha-stats em { + color: black; +} + +#mocha-stats a { + text-decoration: none; + color: inherit; +} + +#mocha-stats a:hover { + border-bottom: 1px solid #eee; +} + +#mocha-stats li { + display: inline-block; + margin: 0 5px; + list-style: none; + padding-top: 11px; +} + +#mocha-stats canvas { + width: 40px; + height: 40px; +} + +#mocha code .comment { color: #ddd; } +#mocha code .init { color: #2f6fad; } +#mocha code .string { color: #5890ad; } +#mocha code .keyword { color: #8a6343; } +#mocha code .number { color: #2f6fad; } + +@media screen and (max-device-width: 480px) { + #mocha { + margin: 60px 0px; + } + + #mocha #stats { + position: absolute; + } +} diff --git a/node_modules/jaro-winkler/test/vendor/mocha.js b/node_modules/jaro-winkler/test/vendor/mocha.js new file mode 100644 index 0000000..82f1c0b --- /dev/null +++ b/node_modules/jaro-winkler/test/vendor/mocha.js @@ -0,0 +1,6467 @@ +;(function(){ + +// CommonJS require() + +function require(p){ + var path = require.resolve(p) + , mod = require.modules[path]; + if (!mod) throw new Error('failed to require "' + p + '"'); + if (!mod.exports) { + mod.exports = {}; + mod.call(mod.exports, mod, mod.exports, require.relative(path)); + } + return mod.exports; + } + +require.modules = {}; + +require.resolve = function (path){ + var orig = path + , reg = path + '.js' + , index = path + '/index.js'; + return require.modules[reg] && reg + || require.modules[index] && index + || orig; + }; + +require.register = function (path, fn){ + require.modules[path] = fn; + }; + +require.relative = function (parent) { + return function(p){ + if ('.' != p.charAt(0)) return require(p); + + var path = parent.split('/') + , segs = p.split('/'); + path.pop(); + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if ('..' == seg) path.pop(); + else if ('.' != seg) path.push(seg); + } + + return require(path.join('/')); + }; + }; + + +require.register("browser/debug.js", function(module, exports, require){ +module.exports = function(type){ + return function(){ + } +}; + +}); // module: browser/debug.js + +require.register("browser/diff.js", function(module, exports, require){ +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +var JsDiff = (function() { + /*jshint maxparams: 5*/ + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + var Diff = function(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + }; + Diff.prototype = { + diff: function(oldString, newString) { + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return [{ value: newString }]; + } + if (!newString) { + return [{ value: oldString, removed: true }]; + } + if (!oldString) { + return [{ value: newString, added: true }]; + } + + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); + + var newLen = newString.length, oldLen = oldString.length; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0 + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { + return bestPath[0].components; + } + + for (var editLength = 1; editLength <= maxEditLength; editLength++) { + for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { + var basePath; + var addPath = bestPath[diagonalPath-1], + removePath = bestPath[diagonalPath+1]; + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath-1] = undefined; + } + + var canAdd = addPath && addPath.newPos+1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + this.pushComponent(basePath.components, oldString[oldPos], undefined, true); + } else { + basePath = clonePath(addPath); + basePath.newPos++; + this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); + } + + var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); + + if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { + return basePath.components; + } else { + bestPath[diagonalPath] = basePath; + } + } + } + }, + + pushComponent: function(components, value, added, removed) { + var last = components[components.length-1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length-1] = + {value: this.join(last.value, value), added: added, removed: removed }; + } else { + components.push({value: value, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath; + while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { + newPos++; + oldPos++; + + this.pushComponent(basePath.components, newString[newPos], undefined, undefined); + } + basePath.newPos = newPos; + return oldPos; + }, + + equals: function(left, right) { + var reWhitespace = /\S/; + if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { + return true; + } else { + return left === right; + } + }, + join: function(left, right) { + return left + right; + }, + tokenize: function(value) { + return value; + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + LineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + + for(var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1]; + + // Merge lines that may contain windows new lines + if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') { + retLines[retLines.length - 1] += '\n'; + } else if (line) { + retLines.push(line); + } + } + + return retLines; + }; + + return { + Diff: Diff, + + diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, + diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, + diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, + diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, + + diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, + + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; + + ret.push('Index: ' + fileName); + ret.push('==================================================================='); + ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + + var diff = LineDiff.diff(oldStr, newStr); + if (!diff[diff.length-1].value) { + diff.pop(); // Remove trailing newline add + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function(entry) { return ' ' + entry; }); + } + function eofNL(curRange, i, current) { + var last = diff[diff.length-2], + isLast = i === diff.length-2, + isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); + + // Figure out if this is the last line for the given file and missing NL + if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); + } + } + + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + if (!oldRangeStart) { + var prev = diff[i-1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); + eofNL(curRange, i, current); + + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length-2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); + } + + oldRangeStart = 0; newRangeStart = 0; curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + } + + return ret.join('\n') + '\n'; + }, + + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'); + var diff = []; + var remEOFNL = false, + addEOFNL = false; + + for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] === '@') { + var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start:meh[3], + oldlength:meh[2], + oldlines:[], + newlength:meh[4], + newlines:[] + }); + } else if(diffstr[i][0] === '+') { + diff[0].newlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '-') { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === ' ') { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if(diffstr[i][0] === '\\') { + if (diffstr[i-1][0] === '+') { + remEOFNL = true; + } else if(diffstr[i-1][0] === '-') { + addEOFNL = true; + } + } + } + + var str = oldStr.split('\n'); + for (var i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if(str[d.start-1+j] !== d.oldlines[j]) { + return false; + } + } + Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + } + + if (remEOFNL) { + while (!str[str.length-1]) { + str.pop(); + } + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); + }, + + convertChangesToXML: function(changes){ + var ret = []; + for ( var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes){ + var ret = [], change; + for ( var i = 0; i < changes.length; i++) { + change = changes[i]; + ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + } + return ret; + } + }; +})(); + +if (typeof module !== 'undefined') { + module.exports = JsDiff; +} + +}); // module: browser/diff.js + +require.register("browser/escape-string-regexp.js", function(module, exports, require){ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; + +}); // module: browser/escape-string-regexp.js + +require.register("browser/events.js", function(module, exports, require){ +/** + * Module exports. + */ + +exports.EventEmitter = EventEmitter; + +/** + * Check if `obj` is an array. + */ + +function isArray(obj) { + return '[object Array]' == {}.toString.call(obj); +} + +/** + * Event emitter constructor. + * + * @api public + */ + +function EventEmitter(){}; + +/** + * Adds a listener. + * + * @api public + */ + +EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; +}; + +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +/** + * Adds a volatile listener. + * + * @api public + */ + +EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + + return this; +}; + +/** + * Removes a listener. + * + * @api public + */ + +EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; +}; + +/** + * Removes all listeners for an event. + * + * @api public + */ + +EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; +}; + +/** + * Gets all listeners for a certain event. + * + * @api public + */ + +EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; +}; + +/** + * Emits an event. + * + * @api public + */ + +EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = [].slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; +}; + +}); // module: browser/events.js + +require.register("browser/fs.js", function(module, exports, require){ + +}); // module: browser/fs.js + +require.register("browser/glob.js", function(module, exports, require){ + +}); // module: browser/glob.js + +require.register("browser/path.js", function(module, exports, require){ + +}); // module: browser/path.js + +require.register("browser/progress.js", function(module, exports, require){ +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ + +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.size = function(n){ + this._size = n; + return this; +}; + +/** + * Set text to `str`. + * + * @param {String} str + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.text = function(str){ + this._text = str; + return this; +}; + +/** + * Set font size to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + * @api public + */ + +Progress.prototype.fontSize = function(n){ + this._fontSize = n; + return this; +}; + +/** + * Set font `family`. + * + * @param {String} family + * @return {Progress} for chaining + */ + +Progress.prototype.font = function(family){ + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {Number} n + * @return {Progress} for chaining + */ + +Progress.prototype.update = function(n){ + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} for chaining + */ + +Progress.prototype.draw = function(ctx){ + try { + var percent = Math.min(this.percent, 100) + , size = this._size + , half = size / 2 + , x = half + , y = half + , rad = half - 1 + , fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%' + , w = ctx.measureText(text).width; + + ctx.fillText( + text + , x - w / 2 + 1 + , y + fontSize / 2 - 1); + } catch (ex) {} //don't fail if we can't render progress + return this; +}; + +}); // module: browser/progress.js + +require.register("browser/tty.js", function(module, exports, require){ +exports.isatty = function(){ + return true; +}; + +exports.getWindowSize = function(){ + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } else { + // In a Web Worker, the DOM Window is not available. + return [640, 480]; + } +}; + +}); // module: browser/tty.js + +require.register("context.js", function(module, exports, require){ +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @api private + */ + +function Context(){} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @param {Runnable} runnable + * @return {Context} + * @api private + */ + +Context.prototype.runnable = function(runnable){ + if (0 == arguments.length) return this._runnable; + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set test timeout `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.timeout = function(ms){ + if (arguments.length === 0) return this.runnable().timeout(); + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Context} self + * @api private + */ + +Context.prototype.enableTimeouts = function (enabled) { + this.runnable().enableTimeouts(enabled); + return this; +}; + + +/** + * Set test slowness threshold `ms`. + * + * @param {Number} ms + * @return {Context} self + * @api private + */ + +Context.prototype.slow = function(ms){ + this.runnable().slow(ms); + return this; +}; + +/** + * Mark a test as skipped. + * + * @return {Context} self + * @api private + */ + +Context.prototype.skip = function(){ + this.runnable().skip(); + return this; +}; + +/** + * Inspect the context void of `._runnable`. + * + * @return {String} + * @api private + */ + +Context.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_runnable' == key) return; + if ('test' == key) return; + return val; + }, 2); +}; + +}); // module: context.js + +require.register("hook.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +function F(){}; +F.prototype = Runnable.prototype; +Hook.prototype = new F; +Hook.prototype.constructor = Hook; + + +/** + * Get or set the test `err`. + * + * @param {Error} err + * @return {Error} + * @api public + */ + +Hook.prototype.error = function(err){ + if (0 == arguments.length) { + var err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; + +}); // module: hook.js + +require.register("interfaces/bdd.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , utils = require('../utils') + , escapeRe = require('browser/escape-string-regexp'); + +/** + * BDD-style interface: + * + * describe('Array', function(){ + * describe('#indexOf()', function(){ + * it('should return -1 when not present', function(){ + * + * }); + * + * it('should return the index when present', function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + var common = require('./common')(suites, context); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending describe. + */ + + context.xdescribe = + context.xcontext = + context.describe.skip = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn){ + var suite = context.describe(title, fn); + mocha.grep(suite.fullTitle()); + return suite; + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn){ + var test = context.it(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + return test; + }; + + /** + * Pending test case. + */ + + context.xit = + context.xspecify = + context.it.skip = function(title){ + context.it(title); + }; + + }); +}; + +}); // module: interfaces/bdd.js + +require.register("interfaces/common.js", function(module, exports, require){ +/** + * Functions common to more than one interface + * @module lib/interfaces/common + */ + +'use strict'; + +module.exports = function (suites, context) { + + return { + /** + * This is only present if flag --delay is passed into Mocha. It triggers + * root suite execution. Returns a function which runs the root suite. + */ + runWithSuite: function runWithSuite(suite) { + return function run() { + suite.run(); + }; + }, + + /** + * Execute before running tests. + */ + before: function (name, fn) { + suites[0].beforeAll(name, fn); + }, + + /** + * Execute after running tests. + */ + after: function (name, fn) { + suites[0].afterAll(name, fn); + }, + + /** + * Execute before each test case. + */ + beforeEach: function (name, fn) { + suites[0].beforeEach(name, fn); + }, + + /** + * Execute after each test case. + */ + afterEach: function (name, fn) { + suites[0].afterEach(name, fn); + }, + + test: { + /** + * Pending test case. + */ + skip: function (title) { + context.test(title); + } + } + } +}; + +}); // module: interfaces/common.js + +require.register("interfaces/exports.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test'); + +/** + * TDD-style interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function(){ + * + * }, + * + * 'should return the correct index when the value is present': function(){ + * + * } + * } + * }; + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('require', visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if ('function' == typeof obj[key]) { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key]); + suites.shift(); + } + } + } +}; + +}); // module: interfaces/exports.js + +require.register("interfaces/index.js", function(module, exports, require){ +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); + +}); // module: interfaces/index.js + +require.register("interfaces/qunit.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('../utils'); + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function(){ + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function(){ + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function(){ + * ok('foo'.length == 3); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + var common = require('./common')(suites, context); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title){ + if (suites.length > 1) suites.shift(); + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + return suite; + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + + }); +}; + +}); // module: interfaces/qunit.js + +require.register("interfaces/tdd.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Suite = require('../suite') + , Test = require('../test') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('../utils'); + +/** + * TDD-style interface: + * + * suite('Array', function(){ + * suite('#indexOf()', function(){ + * suiteSetup(function(){ + * + * }); + * + * test('should return -1 when not present', function(){ + * + * }); + * + * test('should return the index when present', function(){ + * + * }); + * + * suiteTeardown(function(){ + * + * }); + * }); + * }); + * + */ + +module.exports = function(suite){ + var suites = [suite]; + + suite.on('pre-require', function(context, file, mocha){ + + var common = require('./common')(suites, context); + + context.setup = common.beforeEach; + context.teardown = common.afterEach; + context.suiteSetup = common.before; + context.suiteTeardown = common.after; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.suite = function(title, fn){ + var suite = Suite.create(suites[0], title); + suite.file = file; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + return suite; + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + + /** + * Exclusive test-case. + */ + + context.suite.only = function(title, fn){ + var suite = context.suite(title, fn); + mocha.grep(suite.fullTitle()); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn){ + var suite = suites[0]; + if (suite.pending) fn = null; + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn){ + var test = context.test(title, fn); + var reString = '^' + escapeRe(test.fullTitle()) + '$'; + mocha.grep(new RegExp(reString)); + }; + + context.test.skip = common.test.skip; + }); +}; + +}); // module: interfaces/tdd.js + +require.register("mocha.js", function(module, exports, require){ +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('browser/path') + , escapeRe = require('browser/escape-string-regexp') + , utils = require('./utils'); + +/** + * Expose `Mocha`. + */ + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (typeof process !== 'undefined' && typeof process.cwd === 'function') { + var join = path.join + , cwd = process.cwd(); + module.paths.push(cwd, join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +exports.utils = utils; +exports.interfaces = require('./interfaces'); +exports.reporters = require('./reporters'); +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +exports.Runner = require('./runner'); +exports.Suite = require('./suite'); +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Return image `name` path. + * + * @param {String} name + * @return {String} + * @api private + */ + +function image(name) { + return __dirname + '/../images/' + name + '.png'; +} + +/** + * Setup mocha with `options`. + * + * Options: + * + * - `ui` name "bdd", "tdd", "exports" etc + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` + * - `globals` array of accepted globals + * - `timeout` timeout in milliseconds + * - `bail` bail on the first test failure + * - `slow` milliseconds to wait before considering a test slow + * - `ignoreLeaks` ignore global leaks + * - `grep` string or regexp to filter tests with + * + * @param {Object} options + * @api public + */ + +function Mocha(options) { + options = options || {}; + this.files = []; + this.options = options; + this.grep(options.grep); + this.suite = new exports.Suite('', new exports.Context); + this.ui(options.ui); + this.bail(options.bail); + this.reporter(options.reporter, options.reporterOptions); + if (null != options.timeout) this.timeout(options.timeout); + this.useColors(options.useColors) + if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts); + if (options.slow) this.slow(options.slow); + + this.suite.on('pre-require', function (context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + exports.run = context.run; + }); +} + +/** + * Enable or disable bailing on the first failure. + * + * @param {Boolean} [bail] + * @api public + */ + +Mocha.prototype.bail = function(bail){ + if (0 == arguments.length) bail = true; + this.suite.bail(bail); + return this; +}; + +/** + * Add test `file`. + * + * @param {String} file + * @api public + */ + +Mocha.prototype.addFile = function(file){ + this.files.push(file); + return this; +}; + +/** + * Set reporter to `reporter`, defaults to "spec". + * + * @param {String|Function} reporter name or constructor + * @param {Object} reporterOptions optional options + * @api public + */ +Mocha.prototype.reporter = function(reporter, reporterOptions){ + if ('function' == typeof reporter) { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + try { _reporter = require('./reporters/' + reporter); } catch (err) {}; + if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; + if (!_reporter && reporter === 'teamcity') + console.warn('The Teamcity reporter was moved to a package named ' + + 'mocha-teamcity-reporter ' + + '(https://npmjs.org/package/mocha-teamcity-reporter).'); + if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); + this._reporter = _reporter; + } + this.options.reporterOptions = reporterOptions; + return this; +}; + +/** + * Set test UI `name`, defaults to "bdd". + * + * @param {String} bdd + * @api public + */ + +Mocha.prototype.ui = function(name){ + name = name || 'bdd'; + this._ui = exports.interfaces[name]; + if (!this._ui) try { this._ui = require(name); } catch (err) {}; + if (!this._ui) throw new Error('invalid interface "' + name + '"'); + this._ui = this._ui(this.suite); + return this; +}; + +/** + * Load registered files. + * + * @api private + */ + +Mocha.prototype.loadFiles = function(fn){ + var self = this; + var suite = this.suite; + var pending = this.files.length; + this.files.forEach(function(file){ + file = path.resolve(file); + suite.emit('pre-require', global, file, self); + suite.emit('require', require(file), file, self); + suite.emit('post-require', global, file, self); + --pending || (fn && fn()); + }); +}; + +/** + * Enable growl support. + * + * @api private + */ + +Mocha.prototype._growl = function(runner, reporter) { + var notify = require('growl'); + + runner.on('end', function(){ + var stats = reporter.stats; + if (stats.failures) { + var msg = stats.failures + ' of ' + runner.total + ' tests failed'; + notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); + } else { + notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { + name: 'mocha' + , title: 'Passed' + , image: image('ok') + }); + } + }); +}; + +/** + * Add regexp to grep, if `re` is a string it is escaped. + * + * @param {RegExp|String} re + * @return {Mocha} + * @api public + */ + +Mocha.prototype.grep = function(re){ + this.options.grep = 'string' == typeof re + ? new RegExp(escapeRe(re)) + : re; + return this; +}; + +/** + * Invert `.grep()` matches. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.invert = function(){ + this.options.invert = true; + return this; +}; + +/** + * Ignore global leaks. + * + * @param {Boolean} ignore + * @return {Mocha} + * @api public + */ + +Mocha.prototype.ignoreLeaks = function(ignore){ + this.options.ignoreLeaks = !!ignore; + return this; +}; + +/** + * Enable global leak checking. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.checkLeaks = function(){ + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Enable growl support. + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.growl = function(){ + this.options.growl = true; + return this; +}; + +/** + * Ignore `globals` array or string. + * + * @param {Array|String} globals + * @return {Mocha} + * @api public + */ + +Mocha.prototype.globals = function(globals){ + this.options.globals = (this.options.globals || []).concat(globals); + return this; +}; + +/** + * Emit color output. + * + * @param {Boolean} colors + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useColors = function(colors){ + if (colors !== undefined) { + this.options.useColors = colors; + } + return this; +}; + +/** + * Use inline diffs rather than +/-. + * + * @param {Boolean} inlineDiffs + * @return {Mocha} + * @api public + */ + +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined + ? inlineDiffs + : false; + return this; +}; + +/** + * Set the timeout in milliseconds. + * + * @param {Number} timeout + * @return {Mocha} + * @api public + */ + +Mocha.prototype.timeout = function(timeout){ + this.suite.timeout(timeout); + return this; +}; + +/** + * Set slowness threshold in milliseconds. + * + * @param {Number} slow + * @return {Mocha} + * @api public + */ + +Mocha.prototype.slow = function(slow){ + this.suite.slow(slow); + return this; +}; + +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + */ + +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined + ? enabled + : true); + return this +}; + +/** + * Makes all tests async (accepting a callback) + * + * @return {Mocha} + * @api public + */ + +Mocha.prototype.asyncOnly = function(){ + this.options.asyncOnly = true; + return this; +}; + +/** + * Disable syntax highlighting (in browser). + * @returns {Mocha} + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Delay root suite execution. + * @returns {Mocha} + * @api public + */ +Mocha.prototype.delay = function delay() { + this.options.delay = true; + return this; +}; + +/** + * Run tests and invoke `fn()` when complete. + * + * @param {Function} fn + * @return {Runner} + * @api public + */ +Mocha.prototype.run = function(fn){ + if (this.files.length) this.loadFiles(); + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite, options.delay); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = false !== options.ignoreLeaks; + runner.asyncOnly = options.asyncOnly; + if (options.grep) runner.grep(options.grep, options.invert); + if (options.globals) runner.globals(options.globals); + if (options.growl) this._growl(runner, reporter); + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; + } + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + + function done(failures) { + if (reporter.done) { + reporter.done(failures, fn); + } else fn && fn(failures); + } + + return runner.run(done); +}; + +}); // module: mocha.js + +require.register("ms.js", function(module, exports, require){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options['long'] ? longFormat(val) : shortFormat(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function shortFormat(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function longFormat(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} + +}); // module: ms.js + +require.register("pending.js", function(module, exports, require){ + +/** + * Expose `Pending`. + */ + +module.exports = Pending; + +/** + * Initialize a new `Pending` error with the given message. + * + * @param {String} message + */ + +function Pending(message) { + this.message = message; +} + +}); // module: pending.js + +require.register("reporters/base.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var tty = require('browser/tty') + , diff = require('browser/diff') + , ms = require('../ms') + , utils = require('../utils') + , supportsColor = process.env ? require('supports-color') : null; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Enable coloring by default, except in the browser interface. + */ + +exports.useColors = process.env + ? (supportsColor || (process.env.MOCHA_COLORS !== undefined)) + : false; + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + 'pass': 90 + , 'fail': 31 + , 'bright pass': 92 + , 'bright fail': 91 + , 'bright yellow': 93 + , 'pending': 36 + , 'suite': 0 + , 'error title': 0 + , 'error message': 31 + , 'error stack': 90 + , 'checkmark': 32 + , 'fast': 90 + , 'medium': 33 + , 'slow': 31 + , 'green': 32 + , 'light': 90 + , 'diff gutter': 90 + , 'diff added': 42 + , 'diff removed': 41 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if ('win32' == process.platform) { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @param {String} type + * @param {String} str + * @return {String} + * @api private + */ + +var color = exports.color = function(type, str) { + if (!exports.useColors) return String(str); + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}; + +/** + * Expose term window size, with some + * defaults for when stderr is not a tty. + */ + +exports.window = { + width: isatty + ? process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1] + : 75 +}; + +/** + * Expose some basic cursor interactions + * that are common among reporters. + */ + +exports.cursor = { + hide: function(){ + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function(){ + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function(){ + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function(){ + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function(){ + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +/** + * Outut the given `failures` as a list. + * + * @param {Array} failures + * @api public + */ + +exports.list = function(failures){ + console.log(); + failures.forEach(function(test, i){ + // format + var fmt = color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var err = test.err + , message = err.message || '' + , stack = err.stack || message + , index = stack.indexOf(message) + message.length + , msg = stack.slice(0, index) + , actual = err.actual + , expected = err.expected + , escape = true; + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff + if (err.showDiff && sameType(actual, expected)) { + + if ('string' !== typeof actual) { + escape = false; + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); + } + + fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + if (exports.inlineDiffs) { + msg += inlineDiff(err, escape); + } else { + msg += unifiedDiff(err, escape); + } + } + + // indent stack trace without msg + stack = stack.slice(index ? index + 1 : index) + .replace(/^/gm, ' '); + + console.log(fmt, (i + 1), test.fullTitle(), msg, stack); + }); +}; + +/** + * Initialize a new `Base` reporter. + * + * All other reporters generally + * inherit from this reporter, providing + * stats such as test duration, number + * of tests passed / failed etc. + * + * @param {Runner} runner + * @api public + */ + +function Base(runner) { + var self = this + , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } + , failures = this.failures = []; + + if (!runner) return; + this.runner = runner; + + runner.stats = stats; + + runner.on('start', function(){ + stats.start = new Date; + }); + + runner.on('suite', function(suite){ + stats.suites = stats.suites || 0; + suite.root || stats.suites++; + }); + + runner.on('test end', function(test){ + stats.tests = stats.tests || 0; + stats.tests++; + }); + + runner.on('pass', function(test){ + stats.passes = stats.passes || 0; + + var medium = test.slow() / 2; + test.speed = test.duration > test.slow() + ? 'slow' + : test.duration > medium + ? 'medium' + : 'fast'; + + stats.passes++; + }); + + runner.on('fail', function(test, err){ + stats.failures = stats.failures || 0; + stats.failures++; + test.err = err; + failures.push(test); + }); + + runner.on('end', function(){ + stats.end = new Date; + stats.duration = new Date - stats.start; + }); + + runner.on('pending', function(){ + stats.pending++; + }); +} + +/** + * Output common epilogue used by many of + * the bundled reporters. + * + * @api public + */ + +Base.prototype.epilogue = function(){ + var stats = this.stats; + var tests; + var fmt; + + console.log(); + + // passes + fmt = color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, + stats.passes || 0, + ms(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.log(fmt, stats.failures); + + Base.list(this.failures); + console.log(); + } + + console.log(); +}; + +/** + * Pad the given `str` to `len`. + * + * @param {String} str + * @param {String} len + * @return {String} + * @api private + */ + +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + + +/** + * Returns an inline diff between 2 strings with coloured ANSI output + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function inlineDiff(err, escape) { + var msg = errorDiff(err, 'WordsWithSpace', escape); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines.map(function(str, i){ + return pad(++i, width) + ' |' + ' ' + str; + }).join('\n'); + } + + // legend + msg = '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns a unified diff between 2 strings + * + * @param {Error} Error with actual/expected + * @return {String} Diff + * @api private + */ + +function unifiedDiff(err, escape) { + var indent = ' '; + function cleanUp(line) { + if (escape) { + line = escapeInvisibles(line); + } + if (line[0] === '+') return indent + colorLines('diff added', line); + if (line[0] === '-') return indent + colorLines('diff removed', line); + if (line.match(/\@\@/)) return null; + if (line.match(/\\ No newline/)) return null; + else return indent + line; + } + function notBlank(line) { + return line != null; + } + var msg = diff.createPatch('string', err.actual, err.expected); + var lines = msg.split('\n').splice(4); + return '\n ' + + colorLines('diff added', '+ expected') + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines.map(cleanUp).filter(notBlank).join('\n'); +} + +/** + * Return a character diff for `err`. + * + * @param {Error} err + * @return {String} + * @api private + */ + +function errorDiff(err, type, escape) { + var actual = escape ? escapeInvisibles(err.actual) : err.actual; + var expected = escape ? escapeInvisibles(err.expected) : err.expected; + return diff['diff' + type](actual, expected).map(function(str){ + if (str.added) return colorLines('diff added', str.value); + if (str.removed) return colorLines('diff removed', str.value); + return str.value; + }).join(''); +} + +/** + * Returns a string with all invisible characters in plain text + * + * @param {String} line + * @return {String} + * @api private + */ +function escapeInvisibles(line) { + return line.replace(/\t/g, '') + .replace(/\r/g, '') + .replace(/\n/g, '\n'); +} + +/** + * Color lines for `str`, using the color `name`. + * + * @param {String} name + * @param {String} str + * @return {String} + * @api private + */ + +function colorLines(name, str) { + return str.split('\n').map(function(str){ + return color(name, str); + }).join('\n'); +} + +/** + * Check that a / b have the same type. + * + * @param {Object} a + * @param {Object} b + * @return {Boolean} + * @api private + */ + +function sameType(a, b) { + a = Object.prototype.toString.call(a); + b = Object.prototype.toString.call(b); + return a == b; +} + +}); // module: reporters/base.js + +require.register("reporters/doc.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Initialize a new `Doc` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Doc(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on('suite', function(suite){ + if (suite.root) return; + ++indents; + console.log('%s
', indent()); + ++indents; + console.log('%s

%s

', indent(), utils.escape(suite.title)); + console.log('%s
', indent()); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + console.log('%s
', indent()); + --indents; + console.log('%s
', indent()); + --indents; + }); + + runner.on('pass', function(test){ + console.log('%s
%s
', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
%s
', indent(), code); + }); + + runner.on('fail', function(test, err){ + console.log('%s
%s
', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
%s
', indent(), code); + console.log('%s
%s
', indent(), utils.escape(err)); + }); +} + +}); // module: reporters/doc.js + +require.register("reporters/dot.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Dot(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , n = -1; + + runner.on('start', function(){ + process.stdout.write('\n '); + }); + + runner.on('pending', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('pending', Base.symbols.dot)); + }); + + runner.on('pass', function(test){ + if (++n % width == 0) process.stdout.write('\n '); + if ('slow' == test.speed) { + process.stdout.write(color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(color(test.speed, Base.symbols.dot)); + } + }); + + runner.on('fail', function(test, err){ + if (++n % width == 0) process.stdout.write('\n '); + process.stdout.write(color('fail', Base.symbols.dot)); + }); + + runner.on('end', function(){ + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Dot.prototype = new F; +Dot.prototype.constructor = Dot; + + +}); // module: reporters/dot.js + +require.register("reporters/html-cov.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var JSONCov = require('./json-cov') + , fs = require('browser/fs'); + +/** + * Expose `HTMLCov`. + */ + +exports = module.exports = HTMLCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTMLCov(runner) { + var jade = require('jade') + , file = __dirname + '/templates/coverage.jade' + , str = fs.readFileSync(file, 'utf8') + , fn = jade.compile(str, { filename: file }) + , self = this; + + JSONCov.call(this, runner, false); + + runner.on('end', function(){ + process.stdout.write(fn({ + cov: self.cov + , coverageClass: coverageClass + })); + }); +} + +/** + * Return coverage class for `n`. + * + * @return {String} + * @api private + */ + +function coverageClass(n) { + if (n >= 75) return 'high'; + if (n >= 50) return 'medium'; + if (n >= 25) return 'low'; + return 'terrible'; +} + +}); // module: reporters/html-cov.js + +require.register("reporters/html.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , Progress = require('../browser/progress') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = ''; + +/** + * Initialize a new `HTML` reporter. + * + * @param {Runner} runner + * @api public + */ + +function HTML(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total + , stat = fragment(statsTemplate) + , items = stat.getElementsByTagName('li') + , passes = items[1].getElementsByTagName('em')[0] + , passesLink = items[1].getElementsByTagName('a')[0] + , failures = items[2].getElementsByTagName('em')[0] + , failuresLink = items[2].getElementsByTagName('a')[0] + , duration = items[3].getElementsByTagName('em')[0] + , canvas = stat.getElementsByTagName('canvas')[0] + , report = fragment('
    ') + , stack = [report] + , progress + , ctx + , root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress; + } + + if (!root) return error('#mocha div missing, add it to your document'); + + // pass toggle + on(passesLink, 'click', function(){ + unhide(); + var name = /pass/.test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test pass'); + }); + + // failure toggle + on(failuresLink, 'click', function(){ + unhide(); + var name = /fail/.test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) hideSuitesWithout('test fail'); + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) progress.size(40); + + runner.on('suite', function(suite){ + if (suite.root) return; + + // suite + var url = self.suiteURL(suite); + var el = fragment('
  • %s

  • ', url, escape(suite.title)); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on('suite end', function(suite){ + if (suite.root) return; + stack.shift(); + }); + + runner.on('fail', function(test, err){ + if ('hook' == test.type) runner.emit('test end', test); + }); + + runner.on('test end', function(test){ + // TODO: add to stats + var percent = stats.tests / this.total * 100 | 0; + if (progress) progress.update(percent).draw(ctx); + + // update stats + var ms = new Date - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + + // test + if ('passed' == test.state) { + var url = self.testURL(test); + var el = fragment('
  • %e%ems

  • ', test.speed, test.title, test.duration, url); + } else if (test.pending) { + var el = fragment('
  • %e

  • ', test.title); + } else { + var el = fragment('
  • %e

  • ', test.title, self.testURL(test)); + var str = test.err.stack || test.err.toString(); + + // FF / Opera do not add the message + if (!~str.indexOf(test.err.message)) { + str = test.err.message + '\n' + str; + } + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if ('[object Error]' == str) str = test.err.message; + + // Safari doesn't give you a stack. Let's at least provide a source line. + if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { + str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; + } + + el.appendChild(fragment('
    %e
    ', str)); + } + + // toggle code + // TODO: defer + if (!test.pending) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function(){ + pre.style.display = 'none' == pre.style.display + ? 'block' + : 'none'; + }); + + var pre = fragment('
    %e
    ', utils.clean(test.fn.toString())); + el.appendChild(pre); + pre.style.display = 'none'; + } + + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) stack[0].appendChild(el); + }); +} + +/** + * Makes a URL, preserving querystring ("search") parameters. + * @param {string} s + * @returns {string} your new URL + */ +var makeUrl = function makeUrl(s) { + var search = window.location.search; + + // Remove previous grep query parameter if present + if (search) { + search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); + } + + return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s); +}; + +/** + * Provide suite URL + * + * @param {Object} [suite] + */ +HTML.prototype.suiteURL = function(suite){ + return makeUrl(suite.fullTitle()); +}; + +/** + * Provide test URL + * + * @param {Object} [test] + */ + +HTML.prototype.testURL = function(test){ + return makeUrl(test.fullTitle()); +}; + +/** + * Display error `msg`. + */ + +function error(msg) { + document.body.appendChild(fragment('
    %s
    ', msg)); +} + +/** + * Return a DOM fragment from `html`. + */ + +function fragment(html) { + var args = arguments + , div = document.createElement('div') + , i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type){ + switch (type) { + case 's': return String(args[i++]); + case 'e': return escape(args[i++]); + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + */ + +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (0 == els.length) suites[i].className += ' hidden'; + } +} + +/** + * Unhide .hidden suites. + */ + +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set `el` text to `str`. + */ + +function text(el, str) { + if (el.textContent) { + el.textContent = str; + } else { + el.innerText = str; + } +} + +/** + * Listen on `event` with callback `fn`. + */ + +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} + +}); // module: reporters/html.js + +require.register("reporters/index.js", function(module, exports, require){ +exports.Base = require('./base'); +exports.Dot = require('./dot'); +exports.Doc = require('./doc'); +exports.TAP = require('./tap'); +exports.JSON = require('./json'); +exports.HTML = require('./html'); +exports.List = require('./list'); +exports.Min = require('./min'); +exports.Spec = require('./spec'); +exports.Nyan = require('./nyan'); +exports.XUnit = require('./xunit'); +exports.Markdown = require('./markdown'); +exports.Progress = require('./progress'); +exports.Landing = require('./landing'); +exports.JSONCov = require('./json-cov'); +exports.HTMLCov = require('./html-cov'); +exports.JSONStream = require('./json-stream'); + +}); // module: reporters/index.js + +require.register("reporters/json-cov.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `JSONCov`. + */ + +exports = module.exports = JSONCov; + +/** + * Initialize a new `JsCoverage` reporter. + * + * @param {Runner} runner + * @param {Boolean} output + * @api public + */ + +function JSONCov(runner, output) { + var self = this + , output = 1 == arguments.length ? true : output; + + Base.call(this, runner); + + var tests = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('end', function(){ + var cov = global._$jscoverage || {}; + var result = self.cov = map(cov); + result.stats = self.stats; + result.tests = tests.map(clean); + result.failures = failures.map(clean); + result.passes = passes.map(clean); + if (!output) return; + process.stdout.write(JSON.stringify(result, null, 2 )); + }); +} + +/** + * Map jscoverage data to a JSON structure + * suitable for reporting. + * + * @param {Object} cov + * @return {Object} + * @api private + */ + +function map(cov) { + var ret = { + instrumentation: 'node-jscoverage' + , sloc: 0 + , hits: 0 + , misses: 0 + , coverage: 0 + , files: [] + }; + + for (var filename in cov) { + var data = coverage(filename, cov[filename]); + ret.files.push(data); + ret.hits += data.hits; + ret.misses += data.misses; + ret.sloc += data.sloc; + } + + ret.files.sort(function(a, b) { + return a.filename.localeCompare(b.filename); + }); + + if (ret.sloc > 0) { + ret.coverage = (ret.hits / ret.sloc) * 100; + } + + return ret; +} + +/** + * Map jscoverage data for a single source file + * to a JSON structure suitable for reporting. + * + * @param {String} filename name of the source file + * @param {Object} data jscoverage coverage data + * @return {Object} + * @api private + */ + +function coverage(filename, data) { + var ret = { + filename: filename, + coverage: 0, + hits: 0, + misses: 0, + sloc: 0, + source: {} + }; + + data.source.forEach(function(line, num){ + num++; + + if (data[num] === 0) { + ret.misses++; + ret.sloc++; + } else if (data[num] !== undefined) { + ret.hits++; + ret.sloc++; + } + + ret.source[num] = { + source: line + , coverage: data[num] === undefined + ? '' + : data[num] + }; + }); + + ret.coverage = ret.hits / ret.sloc * 100; + + return ret; +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} + +}); // module: reporters/json-cov.js + +require.register("reporters/json-stream.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , total = runner.total; + + runner.on('start', function(){ + console.log(JSON.stringify(['start', { total: total }])); + }); + + runner.on('pass', function(test){ + console.log(JSON.stringify(['pass', clean(test)])); + }); + + runner.on('fail', function(test, err){ + test = clean(test); + test.err = err.message; + console.log(JSON.stringify(['fail', test])); + }); + + runner.on('end', function(){ + process.stdout.write(JSON.stringify(['end', self.stats])); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title + , fullTitle: test.fullTitle() + , duration: test.duration + } +} + +}); // module: reporters/json-stream.js + +require.register("reporters/json.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Initialize a new `JSON` reporter. + * + * @param {Runner} runner + * @api public + */ + +function JSONReporter(runner) { + var self = this; + Base.call(this, runner); + + var tests = [] + , pending = [] + , failures = [] + , passes = []; + + runner.on('test end', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + passes.push(test); + }); + + runner.on('fail', function(test){ + failures.push(test); + }); + + runner.on('pending', function(test){ + pending.push(test); + }); + + runner.on('end', function(){ + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) + } +} + +/** + * Transform `error` into a JSON object. + * @param {Error} err + * @return {Object} + */ + +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + +}); // module: reporters/json.js + +require.register("reporters/landing.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Initialize a new `Landing` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Landing(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , total = runner.total + , stream = process.stdout + , plane = color('plane', '✈') + , crashed = -1 + , n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on('start', function(){ + stream.write('\n\n\n '); + cursor.hide(); + }); + + runner.on('test end', function(test){ + // check if the plane crashed + var col = -1 == crashed + ? width * ++n / total | 0 + : crashed; + + // show the crash + if ('failed' == test.state) { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b['+(width+1)+'D\u001b[2A'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane) + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Landing.prototype = new F; +Landing.prototype.constructor = Landing; + + +}); // module: reporters/landing.js + +require.register("reporters/list.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Initialize a new `List` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function List(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 0; + + runner.on('start', function(){ + console.log(); + }); + + runner.on('test', function(test){ + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on('pending', function(test){ + var fmt = color('checkmark', ' -') + + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on('pass', function(test){ + var fmt = color('checkmark', ' '+Base.symbols.dot) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +List.prototype = new F; +List.prototype.constructor = List; + + +}); // module: reporters/list.js + +require.register("reporters/markdown.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils'); + +/** + * Constants + */ + +var SUITE_PREFIX = '$'; + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Initialize a new `Markdown` reporter. + * + * @param {Runner} runner + * @api public + */ + +function Markdown(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , level = 0 + , buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function indent() { + return Array(level).join(' '); + } + + function mapTOC(suite, obj) { + var ret = obj, + key = SUITE_PREFIX + suite.title; + obj = obj[key] = obj[key] || { suite: suite }; + suite.suites.forEach(function(suite){ + mapTOC(suite, obj); + }); + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if ('suite' == key) continue; + if (key !== SUITE_PREFIX) { + link = ' - [' + key.substring(1) + ']'; + link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + buf += Array(level).join(' ') + link; + } + buf += stringifyTOC(obj[key], level); + } + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on('suite', function(suite){ + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on('suite end', function(suite){ + --level; + }); + + runner.on('pass', function(test){ + var code = utils.clean(test.fn.toString()); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.on('end', function(){ + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} + +}); // module: reporters/markdown.js + +require.register("reporters/min.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Initialize a new `Min` minimal test reporter (best used with --watch). + * + * @param {Runner} runner + * @api public + */ + +function Min(runner) { + Base.call(this, runner); + + runner.on('start', function(){ + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.on('end', this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Min.prototype = new F; +Min.prototype.constructor = Min; + + +}); // module: reporters/min.js + +require.register("reporters/nyan.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base'); + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Initialize a new `Dot` matrix test reporter. + * + * @param {Runner} runner + * @api public + */ + +function NyanCat(runner) { + Base.call(this, runner); + var self = this + , stats = this.stats + , width = Base.window.width * .75 | 0 + , rainbowColors = this.rainbowColors = self.generateColors() + , colorIndex = this.colorIndex = 0 + , numerOfLines = this.numberOfLines = 4 + , trajectories = this.trajectories = [[], [], [], []] + , nyanCatWidth = this.nyanCatWidth = 11 + , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) + , scoreboardWidth = this.scoreboardWidth = 5 + , tick = this.tick = 0 + , n = 0; + + runner.on('start', function(){ + Base.cursor.hide(); + self.draw(); + }); + + runner.on('pending', function(test){ + self.draw(); + }); + + runner.on('pass', function(test){ + self.draw(); + }); + + runner.on('fail', function(test, err){ + self.draw(); + }); + + runner.on('end', function(){ + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) write('\n'); + self.epilogue(); + }); +} + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.draw = function(){ + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @api private + */ + +NyanCat.prototype.drawScoreboard = function(){ + var stats = this.stats; + + function draw(type, n) { + write(' '); + write(Base.color(type, n)); + write('\n'); + } + + draw('green', stats.passes); + draw('fail', stats.failures); + draw('pending', stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @api private + */ + +NyanCat.prototype.appendRainbow = function(){ + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @api private + */ + +NyanCat.prototype.drawRainbow = function(){ + var self = this; + + this.trajectories.forEach(function(line, index) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @api private + */ + +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var dist = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(dist); + write('_,------,'); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(dist); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + var face; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @return {String} + * @api private + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if(stats.passes) { + return '( ^ .^)'; + } else { + return '( - .-)'; + } +}; + +/** + * Move cursor up `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @param {Number} n + * @api private + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @return {Array} + * @api private + */ + +NyanCat.prototype.generateColors = function(){ + var colors = []; + + for (var i = 0; i < (6 * 7); i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = (i * (1.0 / 6)); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +NyanCat.prototype.rainbowify = function(str){ + if (!Base.useColors) + return str; + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + */ + +function write(string) { + process.stdout.write(string); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +NyanCat.prototype = new F; +NyanCat.prototype.constructor = NyanCat; + + +}); // module: reporters/nyan.js + +require.register("reporters/progress.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Initialize a new `Progress` bar test reporter. + * + * @param {Runner} runner + * @param {Object} options + * @api public + */ + +function Progress(runner, options) { + Base.call(this, runner); + + var self = this + , options = options || {} + , stats = this.stats + , width = Base.window.width * .50 | 0 + , total = runner.total + , complete = 0 + , max = Math.max + , lastN = -1; + + // default chars + options.open = options.open || '['; + options.complete = options.complete || '▬'; + options.incomplete = options.incomplete || Base.symbols.dot; + options.close = options.close || ']'; + options.verbose = false; + + // tests started + runner.on('start', function(){ + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on('test end', function(){ + complete++; + var incomplete = total - complete + , percent = complete / total + , n = width * percent | 0 + , i = width - n; + + if (lastN === n && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.on('end', function(){ + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Progress.prototype = new F; +Progress.prototype.constructor = Progress; + + +}); // module: reporters/progress.js + +require.register("reporters/spec.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Initialize a new `Spec` test reporter. + * + * @param {Runner} runner + * @api public + */ + +function Spec(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , indents = 0 + , n = 0; + + function indent() { + return Array(indents).join(' ') + } + + runner.on('start', function(){ + console.log(); + }); + + runner.on('suite', function(suite){ + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on('suite end', function(suite){ + --indents; + if (1 == indents) console.log(); + }); + + runner.on('pending', function(test){ + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on('pass', function(test){ + if ('fast' == test.speed) { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s '); + cursor.CR(); + console.log(fmt, test.title); + } else { + var fmt = indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s ') + + color(test.speed, '(%dms)'); + cursor.CR(); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on('fail', function(test, err){ + cursor.CR(); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.on('end', self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +Spec.prototype = new F; +Spec.prototype.constructor = Spec; + + +}); // module: reporters/spec.js + +require.register("reporters/tap.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , cursor = Base.cursor + , color = Base.color; + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Initialize a new `TAP` reporter. + * + * @param {Runner} runner + * @api public + */ + +function TAP(runner) { + Base.call(this, runner); + + var self = this + , stats = this.stats + , n = 1 + , passes = 0 + , failures = 0; + + runner.on('start', function(){ + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); + }); + + runner.on('test end', function(){ + ++n; + }); + + runner.on('pending', function(test){ + console.log('ok %d %s # SKIP -', n, title(test)); + }); + + runner.on('pass', function(test){ + passes++; + console.log('ok %d %s', n, title(test)); + }); + + runner.on('fail', function(test, err){ + failures++; + console.log('not ok %d %s', n, title(test)); + if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); + }); + + runner.on('end', function(){ + console.log('# tests ' + (passes + failures)); + console.log('# pass ' + passes); + console.log('# fail ' + failures); + }); +} + +/** + * Return a TAP-safe title of `test` + * + * @param {Object} test + * @return {String} + * @api private + */ + +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} + +}); // module: reporters/tap.js + +require.register("reporters/xunit.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Base = require('./base') + , utils = require('../utils') + , fs = require('browser/fs') + , escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Initialize a new `XUnit` reporter. + * + * @param {Runner} runner + * @api public + */ + +function XUnit(runner, options) { + Base.call(this, runner); + var stats = this.stats + , tests = [] + , self = this; + + if (options.reporterOptions && options.reporterOptions.output) { + if (! fs.createWriteStream) { + throw new Error('file output not supported in browser'); + } + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + + runner.on('pending', function(test){ + tests.push(test); + }); + + runner.on('pass', function(test){ + tests.push(test); + }); + + runner.on('fail', function(test){ + tests.push(test); + }); + + runner.on('end', function(){ + self.write(tag('testsuite', { + name: 'Mocha Tests' + , tests: stats.tests + , failures: stats.failures + , errors: stats.failures + , skipped: stats.tests - stats.failures - stats.passes + , timestamp: (new Date).toUTCString() + , time: (stats.duration / 1000) || 0 + }, false)); + + tests.forEach(function(t) { self.test(t); }); + self.write(''); + }); +} + +/** + * Override done to close the stream (if it's a file). + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + +/** + * Inherit from `Base.prototype`. + */ + +function F(){}; +F.prototype = Base.prototype; +XUnit.prototype = new F; +XUnit.prototype.constructor = XUnit; + + +/** + * Write out the given line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else { + console.log(line); + } +}; + +/** + * Output tag for the given `test.` + */ + +XUnit.prototype.test = function(test, ostream) { + var attrs = { + classname: test.parent.fullTitle() + , name: test.title + , time: (test.duration / 1000) || 0 + }; + + if ('failed' == test.state) { + var err = test.err; + this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); + } else if (test.pending) { + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + this.write(tag('testcase', attrs, true) ); + } +}; + +/** + * HTML tag helper. + */ + +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>' + , pairs = [] + , tag; + + for (var key in attrs) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) tag += content + ''; +} + +}); // module: reporters/xunit.js + +require.register("runnable.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:runnable') + , Pending = require('./pending') + , milliseconds = require('./ms') + , utils = require('./utils'); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date + , setTimeout = global.setTimeout + , setInterval = global.setInterval + , clearTimeout = global.clearTimeout + , clearInterval = global.clearInterval; + +/** + * Object#toString(). + */ + +var toString = Object.prototype.toString; + +/** + * Expose `Runnable`. + */ + +module.exports = Runnable; + +/** + * Initialize a new `Runnable` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Runnable(title, fn) { + this.title = title; + this.fn = fn; + this.async = fn && fn.length; + this.sync = ! this.async; + this._timeout = 2000; + this._slow = 75; + this._enableTimeouts = true; + this.timedOut = false; + this._trace = new Error('done() called multiple times') +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Runnable.prototype = new F; +Runnable.prototype.constructor = Runnable; + + +/** + * Set & get timeout `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) this.resetTimeout(); + return this; +}; + +/** + * Set & get slow `ms`. + * + * @param {Number|String} ms + * @return {Runnable|Number} ms or self + * @api private + */ + +Runnable.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and & get timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Runnable|Boolean} enabled or self + * @api private + */ + +Runnable.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Halt and mark as pending. + * + * @api private + */ + +Runnable.prototype.skip = function(){ + throw new Pending(); +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Runnable.prototype.fullTitle = function(){ + return this.parent.fullTitle() + ' ' + this.title; +}; + +/** + * Clear the timeout. + * + * @api private + */ + +Runnable.prototype.clearTimeout = function(){ + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @return {String} + * @api private + */ + +Runnable.prototype.inspect = function(){ + return JSON.stringify(this, function(key, val){ + if ('_' == key[0]) return; + if ('parent' == key) return '#'; + if ('ctx' == key) return '#'; + return val; + }, 2); +}; + +/** + * Reset the timeout. + * + * @api private + */ + +Runnable.prototype.resetTimeout = function(){ + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) return; + this.clearTimeout(); + this.timer = setTimeout(function(){ + if (!self._enableTimeouts) return; + self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.')); + self.timedOut = true; + }, ms); +}; + +/** + * Whitelist these globals for this test run + * + * @api private + */ +Runnable.prototype.globals = function(arr){ + var self = this; + this._allowedGlobals = arr; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runnable.prototype.run = function(fn){ + var self = this + , start = new Date + , ctx = this.ctx + , finished + , emitted; + + // Some times the ctx exists but it is not runnable + if (ctx && ctx.runnable) ctx.runnable(this); + + // called multiple times + function multiple(err) { + if (emitted) return; + emitted = true; + self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate')); + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) return; + if (finished) return multiple(err || self._trace); + self.clearTimeout(); + self.duration = new Date - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'); + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + try { + this.fn.call(ctx, function(err){ + if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); + if (null != err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err))); + } else { + return done(new Error('done() invoked with non-Error: ' + err)); + } + } + done(); + }); + } catch (err) { + done(utils.getError(err)); + } + return; + } + + if (this.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()`')); + } + + // sync or promise-returning + try { + if (this.pending) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + done(utils.getError(err)); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result + .then(function() { + done() + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')) + }); + } else { + done(); + } + } +}; + +}); // module: runnable.js + +require.register("runner.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:runner') + , Pending = require('./pending') + , Test = require('./test') + , utils = require('./utils') + , filter = utils.filter + , keys = utils.keys + , type = utils.type + , stringify = utils.stringify; + +/** + * Non-enumerable globals. + */ + +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date', + 'setImmediate', + 'clearImmediate' +]; + +/** + * Expose `Runner`. + */ + +module.exports = Runner; + +/** + * Initialize a `Runner` for the given `suite`. + * + * Events: + * + * - `start` execution started + * - `end` execution complete + * - `suite` (suite) test suite execution started + * - `suite end` (suite) all tests (and sub-suites) have finished + * - `test` (test) test execution started + * - `test end` (test) test completed + * - `hook` (hook) hook execution started + * - `hook end` (hook) hook complete + * - `pass` (test) test passed + * - `fail` (test, err) test failed + * - `pending` (test) test pending + * + * @param {Suite} suite Root suite + * @param {boolean} [delay] Whether or not to delay execution of root suite + * until ready. + * @api public + */ + +function Runner(suite, delay) { + var self = this; + this._globals = []; + this._abort = false; + this._delay = delay; + this.suite = suite; + this.total = suite.total(); + this.failures = 0; + this.on('test end', function(test){ self.checkGlobals(test); }); + this.on('hook end', function(hook){ self.checkGlobals(hook); }); + this.grep(/.*/); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @api private + */ + +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Runner.prototype = new F; +Runner.prototype.constructor = Runner; + + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @param {RegExp} re + * @param {Boolean} invert + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.grep = function(re, invert){ + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @param {Suite} suite + * @return {Number} + * @api public + */ + +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test){ + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (match) total++; + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @api private + */ + +Runner.prototype.globalProps = function() { + var props = utils.keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~utils.indexOf(props, globals[i])) continue; + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @param {Array} arr + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.globals = function(arr){ + if (0 == arguments.length) return this._globals; + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @api private + */ + +Runner.prototype.checkGlobals = function(test){ + if (this.ignoreLeaks) return; + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if(this.prevGlobalsLength == globals.length) return; + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length > 1) { + this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); + } else if (leaks.length) { + this.fail(test, new Error('global leak detected: ' + leaks[0])); + } +}; + +/** + * Fail the given `test`. + * + * @param {Test} test + * @param {Error} err + * @api private + */ + +Runner.prototype.fail = function(test, err){ + ++this.failures; + test.state = 'failed'; + + if ('string' == typeof err) { + err = new Error('the string "' + err + '" was thrown, throw an Error :)'); + } else if (!(err instanceof Error)) { + err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'); + } + + this.emit('fail', test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @param {Hook} hook + * @param {Error} err + * @api private + */ + +Runner.prototype.failHook = function(hook, err){ + this.fail(hook, err); + if (this.suite.bail()) { + this.emit('end'); + } +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @param {String} name + * @param {Function} function + * @api private + */ + +Runner.prototype.hook = function(name, fn){ + var suite = this.suite + , hooks = suite['_' + name] + , self = this + , timer; + + function next(i) { + var hook = hooks[i]; + if (!hook) return fn(); + self.currentRunnable = hook; + + hook.ctx.currentTest = self.test; + + self.emit('hook', hook); + + hook.on('error', function(err){ + self.failHook(hook, err); + }); + + hook.run(function(err){ + hook.removeAllListeners('error'); + var testError = hook.error(); + if (testError) self.fail(self.test, testError); + if (err) { + if (err instanceof Pending) { + suite.pending = true; + } else { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + } + self.emit('hook end', hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function(){ + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @param {String} name + * @param {Array} suites + * @param {Function} fn + * @api private + */ + +Runner.prototype.hooks = function(name, suites, fn){ + var self = this + , orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err){ + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookUp = function(name, fn){ + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @api private + */ + +Runner.prototype.hookDown = function(name, fn){ + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @api private + */ + +Runner.prototype.parents = function(){ + var suite = this.suite + , suites = []; + while (suite = suite.parent) suites.push(suite); + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTest = function(fn){ + var test = this.test + , self = this; + + if (this.asyncOnly) test.asyncOnly = true; + + try { + test.on('error', function(err){ + self.fail(test, err); + }); + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke + * the callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runTests = function(suite, fn){ + var self = this + , tests = suite.tests.slice() + , test; + + + function hookErr(err, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp('afterEach', function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) return hookErr(err2, errSuite2, true); + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) return fn(); + + if (self._abort) return fn(); + + if (err) return hookErr(err, errSuite, true); + + // next test + test = tests.shift(); + + // all done + if (!test) return fn(); + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) match = !match; + if (!match) return next(); + + // pending + if (test.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + + // execute test and hook(s) + self.emit('test', self.test = test); + self.hookDown('beforeEach', function(err, errSuite){ + + if (suite.pending) { + self.emit('pending', test); + self.emit('test end', test); + return next(); + } + if (err) return hookErr(err, errSuite, false); + + self.currentRunnable = self.test; + self.runTest(function(err){ + test = self.test; + + if (err) { + if (err instanceof Pending) { + self.emit('pending', test); + } else { + self.fail(test, err); + } + self.emit('test end', test); + + if (err instanceof Pending) { + return next(); + } + + return self.hookUp('afterEach', next); + } + + test.state = 'passed'; + self.emit('pass', test); + self.emit('test end', test); + self.hookUp('afterEach', next); + }); + }); + } + + this.next = next; + next(); +}; + +/** + * Run the given `suite` and invoke the + * callback `fn()` when complete. + * + * @param {Suite} suite + * @param {Function} fn + * @api private + */ + +Runner.prototype.runSuite = function(suite, fn){ + var total = this.grepTotal(suite) + , self = this + , i = 0; + + debug('run suite %s', suite.fullTitle()); + + if (!total) return fn(); + + this.emit('suite', this.suite = suite); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite == suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } else { + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + } + + if (self._abort) return done(); + + var curr = suite.suites[i++]; + if (!curr) return done(); + self.runSuite(curr, next); + } + + function done(errSuite) { + self.suite = suite; + self.hook('afterAll', function(){ + self.emit('suite end', suite); + fn(errSuite); + }); + } + + this.hook('beforeAll', function(err){ + if (err) return done(); + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @api private + */ + +Runner.prototype.uncaught = function(err){ + if (err) { + debug('uncaught exception %s', err !== function () { + return this; + }.call(err) ? err : ( err.message || err )); + } else { + debug('uncaught undefined exception'); + err = utils.undefinedError(); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + if (!runnable) return; + + var wasAlreadyDone = runnable.state; + this.fail(runnable, err); + + runnable.clearTimeout(); + + if (wasAlreadyDone) return; + + // recover from test + if ('test' == runnable.type) { + this.emit('test end', runnable); + this.hookUp('afterEach', this.next); + return; + } + + // bail on hooks + this.emit('end'); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @param {Function} fn + * @return {Runner} for chaining + * @api public + */ + +Runner.prototype.run = function(fn){ + var self = this, + rootSuite = this.suite; + + fn = fn || function(){}; + + function uncaught(err){ + self.uncaught(err); + } + + function start() { + self.emit('start'); + self.runSuite(rootSuite, function(){ + debug('finished running'); + self.emit('end'); + }); + } + + debug('start'); + + // callback + this.on('end', function(){ + debug('end'); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + if (this._delay) { + // for reporters, I guess. + // might be nice to debounce some dots while we wait. + this.emit('waiting', rootSuite); + rootSuite.once('run', start); + } + else { + start(); + } + + return this; +}; + +/** + * Cleanly abort execution + * + * @return {Runner} for chaining + * @api public + */ +Runner.prototype.abort = function(){ + debug('aborting'); + this._abort = true; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @param {Array} ok + * @param {Array} globals + * @return {Array} + * @api private + */ + +function filterLeaks(ok, globals) { + return filter(globals, function(key){ + // Firefox and Chrome exposes iframes as index inside the window object + if (/^d+/.test(key)) return false; + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method not init at first + // it is assigned in some seconds + if (global.navigator && /^getInterface/.test(key)) return false; + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && /^\d+/.test(key)) return false; + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) return false; + + var matched = filter(ok, function(ok){ + if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); + return key == ok; + }); + return matched.length == 0 && (!global.navigator || 'onerror' !== key); + }); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @api private + */ + + function extraGlobals() { + if (typeof(process) === 'object' && + typeof(process.version) === 'string') { + + var nodeVersion = process.version.split('.').reduce(function(a, v) { + return a << 8 | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + + if (nodeVersion < 0x00090B) { + return ['errno']; + } + } + + return []; + } + +}); // module: runner.js + +require.register("suite.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var EventEmitter = require('browser/events').EventEmitter + , debug = require('browser/debug')('mocha:suite') + , milliseconds = require('./ms') + , utils = require('./utils') + , Hook = require('./hook'); + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` + * and parent `Suite`. When a suite with the + * same title is already present, that suite + * is returned to provide nicer reporter + * and more flexible meta-testing. + * + * @param {Suite} parent + * @param {String} title + * @return {Suite} + * @api public + */ + +exports.create = function(parent, title){ + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + if (parent.pending) suite.pending = true; + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Initialize a new `Suite` with the given + * `title` and `ctx`. + * + * @param {String} title + * @param {Context} ctx + * @api private + */ + +function Suite(title, parentContext) { + this.title = title; + var context = function() {}; + context.prototype = parentContext; + this.ctx = new context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = !title; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; + this.delayed = false; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +function F(){}; +F.prototype = EventEmitter.prototype; +Suite.prototype = new F; +Suite.prototype.constructor = Suite; + + +/** + * Return a clone of this `Suite`. + * + * @return {Suite} + * @api private + */ + +Suite.prototype.clone = function(){ + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set timeout `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.timeout = function(ms){ + if (0 == arguments.length) return this._timeout; + if (ms.toString() === '0') this._enableTimeouts = false; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Suite|Boolean} self or enabled + * @api private + */ + +Suite.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set slow `ms` or short-hand such as "2s". + * + * @param {Number|String} ms + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.slow = function(ms){ + if (0 === arguments.length) return this._slow; + if ('string' == typeof ms) ms = milliseconds(ms); + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Sets whether to bail after first error. + * + * @param {Boolean} bail + * @return {Suite|Number} for chaining + * @api private + */ + +Suite.prototype.bail = function(bail){ + if (0 == arguments.length) return this._bail; + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeAll.push(hook); + this.emit('beforeAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterAll = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterAll.push(hook); + this.emit('afterAll', hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.beforeEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._beforeEach.push(hook); + this.emit('beforeEach', hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @param {Function} fn + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.afterEach = function(title, fn){ + if (this.pending) return this; + if ('function' === typeof title) { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + this._afterEach.push(hook); + this.emit('afterEach', hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @param {Suite} suite + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addSuite = function(suite){ + suite.parent = this; + suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit('suite', suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @param {Test} test + * @return {Suite} for chaining + * @api private + */ + +Suite.prototype.addTest = function(test){ + test.parent = this; + test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit('test', test); + return this; +}; + +/** + * Return the full title generated by recursively + * concatenating the parent's full title. + * + * @return {String} + * @api public + */ + +Suite.prototype.fullTitle = function(){ + if (this.parent) { + var full = this.parent.fullTitle(); + if (full) return full + ' ' + this.title; + } + return this.title; +}; + +/** + * Return the total number of tests. + * + * @return {Number} + * @api public + */ + +Suite.prototype.total = function(){ + return utils.reduce(this.suites, function(sum, suite){ + return sum + suite.total(); + }, 0) + this.tests.length; +}; + +/** + * Iterates through each suite recursively to find + * all tests. Applies a function in the format + * `fn(test)`. + * + * @param {Function} fn + * @return {Suite} + * @api private + */ + +Suite.prototype.eachTest = function(fn){ + utils.forEach(this.tests, fn); + utils.forEach(this.suites, function(suite){ + suite.eachTest(fn); + }); + return this; +}; + +/** + * This will run the root suite if we happen to be running in delayed mode. + */ +Suite.prototype.run = function run() { + if (this.root) { + this.emit('run'); + } +}; + +}); // module: suite.js + +require.register("test.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var Runnable = require('./runnable'); + +/** + * Expose `Test`. + */ + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @param {String} title + * @param {Function} fn + * @api private + */ + +function Test(title, fn) { + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ + +function F(){}; +F.prototype = Runnable.prototype; +Test.prototype = new F; +Test.prototype.constructor = Test; + + +}); // module: test.js + +require.register("utils.js", function(module, exports, require){ +/** + * Module dependencies. + */ + +var fs = require('browser/fs') + , path = require('browser/path') + , basename = path.basename + , exists = fs.existsSync || path.existsSync + , glob = require('browser/glob') + , join = path.join + , debug = require('browser/debug')('mocha:watch'); + +/** + * Ignored directories. + */ + +var ignore = ['node_modules', '.git']; + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Array#forEach (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.forEach = function(arr, fn, scope){ + for (var i = 0, l = arr.length; i < l; i++) + fn.call(scope, arr[i], i); +}; + +/** + * Array#map (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} scope + * @api private + */ + +exports.map = function(arr, fn, scope){ + var result = []; + for (var i = 0, l = arr.length; i < l; i++) + result.push(fn.call(scope, arr[i], i, arr)); + return result; +}; + +/** + * Array#indexOf (<=IE8) + * + * @parma {Array} arr + * @param {Object} obj to find index of + * @param {Number} start + * @api private + */ + +exports.indexOf = function(arr, obj, start){ + for (var i = start || 0, l = arr.length; i < l; i++) { + if (arr[i] === obj) + return i; + } + return -1; +}; + +/** + * Array#reduce (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @param {Object} initial value + * @api private + */ + +exports.reduce = function(arr, fn, val){ + var rval = val; + + for (var i = 0, l = arr.length; i < l; i++) { + rval = fn(rval, arr[i], i, arr); + } + + return rval; +}; + +/** + * Array#filter (<=IE8) + * + * @param {Array} array + * @param {Function} fn + * @api private + */ + +exports.filter = function(arr, fn){ + var ret = []; + + for (var i = 0, l = arr.length; i < l; i++) { + var val = arr[i]; + if (fn(val, i, arr)) ret.push(val); + } + + return ret; +}; + +/** + * Object.keys (<=IE8) + * + * @param {Object} obj + * @return {Array} keys + * @api private + */ + +exports.keys = Object.keys || function(obj) { + var keys = [] + , has = Object.prototype.hasOwnProperty; // for `window` on <=IE8 + + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + + return keys; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @param {Array} files + * @param {Function} fn + * @api private + */ + +exports.watch = function(files, fn){ + var options = { interval: 100 }; + files.forEach(function(file){ + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev){ + if (prev.mtime < curr.mtime) fn(file); + }); + }); +}; + +/** + * Array.isArray (<=IE8) + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ +var isArray = Array.isArray || function (obj) { + return '[object Array]' == {}.toString.call(obj); +}; + +/** + * @description + * Buffer.prototype.toJSON polyfill + * @type {Function} + */ +if(typeof Buffer !== 'undefined' && Buffer.prototype) { + Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () { + return Array.prototype.slice.call(this, 0); + }; +} + +/** + * Ignored files. + */ + +function ignored(path){ + return !~ignore.indexOf(path); +} + +/** + * Lookup files in the given `dir`. + * + * @return {Array} + * @api private + */ + +exports.files = function(dir, ext, ret){ + ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); + + fs.readdirSync(dir) + .filter(ignored) + .forEach(function(path){ + path = join(dir, path); + if (fs.statSync(path).isDirectory()) { + exports.files(path, ext, ret); + } else if (path.match(re)) { + ret.push(path); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.slug = function(str){ + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, + * and re-indent for pre whitespace. + */ + +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') + .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '') + .replace(/\s+\}$/, ''); + + var spaces = str.match(/^\n?( *)/)[1].length + , tabs = str.match(/^\n?(\t*)/)[1].length + , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); + + str = str.replace(re, ''); + + return exports.trim(str); +}; + +/** + * Trim the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.trim = function(str){ + return str.replace(/^\s+|\s+$/g, ''); +}; + +/** + * Parse the given `qs`. + * + * @param {String} qs + * @return {Object} + * @api private + */ + +exports.parseQuery = function(qs){ + return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ + var i = pair.indexOf('=') + , key = pair.slice(0, i) + , val = pair.slice(++i); + + obj[key] = decodeURIComponent(val); + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @param {String} js + * @return {String} + * @api private + */ + +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') + .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') +} + +/** + * Highlight the contents of tag `name`. + * + * @param {String} name + * @api private + */ + +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + +/** + * If a value could have properties, and has none, this function is called, which returns + * a string representation of the empty value. + * + * Functions w/ no properties return `'[Function]'` + * Arrays w/ length === 0 return `'[]'` + * Objects w/ no properties return `'{}'` + * All else: return result of `value.toString()` + * + * @param {*} value Value to inspect + * @param {string} [type] The type of the value, if known. + * @returns {string} + */ +var emptyRepresentation = function emptyRepresentation(value, type) { + type = type || exports.type(value); + + switch(type) { + case 'function': + return '[Function]'; + case 'object': + return '{}'; + case 'array': + return '[]'; + default: + return value.toString(); + } +}; + +/** + * Takes some variable and asks `{}.toString()` what it thinks it is. + * @param {*} value Anything + * @example + * type({}) // 'object' + * type([]) // 'array' + * type(1) // 'number' + * type(false) // 'boolean' + * type(Infinity) // 'number' + * type(null) // 'null' + * type(new Date()) // 'date' + * type(/foo/) // 'regexp' + * type('type') // 'string' + * type(global) // 'global' + * @api private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + * @returns {string} + */ +exports.type = function type(value) { + if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + return 'buffer'; + } + return Object.prototype.toString.call(value) + .replace(/^\[.+\s(.+?)\]$/, '$1') + .toLowerCase(); +}; + +/** + * @summary Stringify `value`. + * @description Different behavior depending on type of value. + * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. + * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. + * - If `value` is an *empty* object, function, or array, return result of function + * {@link emptyRepresentation}. + * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of + * JSON.stringify(). + * + * @see exports.type + * @param {*} value + * @return {string} + * @api private + */ + +exports.stringify = function(value) { + var type = exports.type(value); + + if (!~exports.indexOf(['object', 'array', 'function'], type)) { + if(type != 'buffer') { + return jsonStringify(value); + } + var json = value.toJSON(); + // Based on the toJSON result + return jsonStringify(json.data && json.type ? json.data : json, 2) + .replace(/,(\n|$)/g, '$1'); + } + + for (var prop in value) { + if (Object.prototype.hasOwnProperty.call(value, prop)) { + return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1'); + } + } + + return emptyRepresentation(value, type); +}; + +/** + * @description + * like JSON.stringify but more sense. + * @param {Object} object + * @param {Number=} spaces + * @param {number=} depth + * @returns {*} + * @private + */ +function jsonStringify(object, spaces, depth) { + if(typeof spaces == 'undefined') return _stringify(object); // primitive types + + depth = depth || 1; + var space = spaces * depth + , str = isArray(object) ? '[' : '{' + , end = isArray(object) ? ']' : '}' + , length = object.length || exports.keys(object).length + , repeat = function(s, n) { return new Array(n).join(s); }; // `.repeat()` polyfill + + function _stringify(val) { + switch (exports.type(val)) { + case 'null': + case 'undefined': + val = '[' + val + ']'; + break; + case 'array': + case 'object': + val = jsonStringify(val, spaces, depth + 1); + break; + case 'boolean': + case 'regexp': + case 'number': + val = val === 0 && (1/val) === -Infinity // `-0` + ? '-0' + : val.toString(); + break; + case 'date': + val = '[Date: ' + val.toISOString() + ']'; + break; + case 'buffer': + var json = val.toJSON(); + // Based on the toJSON result + json = json.data && json.type ? json.data : json; + val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; + break; + default: + val = (val == '[Function]' || val == '[Circular]') + ? val + : '"' + val + '"'; //string + } + return val; + } + + for(var i in object) { + if(!object.hasOwnProperty(i)) continue; // not my business + --length; + str += '\n ' + repeat(' ', space) + + (isArray(object) ? '' : '"' + i + '": ') // key + + _stringify(object[i]) // value + + (length ? ',' : ''); // comma + } + + return str + (str.length != 1 // [], {} + ? '\n' + repeat(' ', --space) + end + : end); +} + +/** + * Return if obj is a Buffer + * @param {Object} arg + * @return {Boolean} + * @api private + */ +exports.isBuffer = function (arg) { + return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg); +}; + +/** + * @summary Return a new Thing that has the keys in sorted order. Recursive. + * @description If the Thing... + * - has already been seen, return string `'[Circular]'` + * - is `undefined`, return string `'[undefined]'` + * - is `null`, return value `null` + * - is some other primitive, return the value + * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method + * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. + * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` + * + * @param {*} value Thing to inspect. May or may not have properties. + * @param {Array} [stack=[]] Stack of seen values + * @return {(Object|Array|Function|string|undefined)} + * @see {@link exports.stringify} + * @api private + */ + +exports.canonicalize = function(value, stack) { + var canonicalizedObj, + type = exports.type(value), + prop, + withStack = function withStack(value, fn) { + stack.push(value); + fn(); + stack.pop(); + }; + + stack = stack || []; + + if (exports.indexOf(stack, value) !== -1) { + return '[Circular]'; + } + + switch(type) { + case 'undefined': + case 'buffer': + case 'null': + canonicalizedObj = value; + break; + case 'array': + withStack(value, function () { + canonicalizedObj = exports.map(value, function (item) { + return exports.canonicalize(item, stack); + }); + }); + break; + case 'function': + for (prop in value) { + canonicalizedObj = {}; + break; + } + if (!canonicalizedObj) { + canonicalizedObj = emptyRepresentation(value, type); + break; + } + /* falls through */ + case 'object': + canonicalizedObj = canonicalizedObj || {}; + withStack(value, function () { + exports.forEach(exports.keys(value).sort(), function (key) { + canonicalizedObj[key] = exports.canonicalize(value[key], stack); + }); + }); + break; + case 'date': + case 'number': + case 'regexp': + case 'boolean': + canonicalizedObj = value; + break; + default: + canonicalizedObj = value.toString(); + } + + return canonicalizedObj; +}; + +/** + * Lookup file names at the given `path`. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); + return files; + } + } + + try { + var stat = fs.statSync(path); + if (stat.isFile()) return path; + } + catch (ignored) { + return; + } + + fs.readdirSync(path).forEach(function(file) { + file = join(path, file); + try { + var stat = fs.statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } + catch (ignored) { + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return; + files.push(file); + }); + + return files; +}; + +/** + * Generate an undefined error with a message warning the user. + * + * @return {Error} + */ + +exports.undefinedError = function() { + return new Error('Caught undefined error, did you throw without specifying what?'); +}; + +/** + * Generate an undefined error if `err` is not defined. + * + * @param {Error} err + * @return {Error} + */ + +exports.getError = function(err) { + return err || exports.undefinedError(); +}; + + +}); // module: utils.js +// The global object is "self" in Web Workers. +var global = (function() { return this; })(); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; + +/** + * Node shims. + * + * These are meant only to allow + * mocha.js to run untouched, not + * to allow running node code in + * the browser. + */ + +var process = {}; +process.exit = function(status){}; +process.stdout = {}; + +var uncaughtExceptionHandlers = []; + +var originalOnerrorHandler = global.onerror; + +/** + * Remove uncaughtException listener. + * Revert to original onerror handler if previously defined. + */ + +process.removeListener = function(e, fn){ + if ('uncaughtException' == e) { + if (originalOnerrorHandler) { + global.onerror = originalOnerrorHandler; + } else { + global.onerror = function() {}; + } + var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); + if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } + } +}; + +/** + * Implements uncaughtException listener. + */ + +process.on = function(e, fn){ + if ('uncaughtException' == e) { + global.onerror = function(err, url, line){ + fn(new Error(err + ' (' + url + ':' + line + ')')); + return true; + }; + uncaughtExceptionHandlers.push(fn); + } +}; + +/** + * Expose mocha. + */ + +var Mocha = global.Mocha = require('mocha'), + mocha = global.mocha = new Mocha({ reporter: 'html' }); + +// The BDD UI is registered by default, but no UI will be functional in the +// browser without an explicit call to the overridden `mocha.ui` (see below). +// Ensure that this default UI does not expose its methods to the global scope. +mocha.suite.removeAllListeners('pre-require'); + +var immediateQueue = [] + , immediateTimeout; + +function timeslice() { + var immediateStart = new Date().getTime(); + while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { + immediateQueue.shift()(); + } + if (immediateQueue.length) { + immediateTimeout = setTimeout(timeslice, 0); + } else { + immediateTimeout = null; + } +} + +/** + * High-performance override of Runner.immediately. + */ + +Mocha.Runner.immediately = function(callback) { + immediateQueue.push(callback); + if (!immediateTimeout) { + immediateTimeout = setTimeout(timeslice, 0); + } +}; + +/** + * Function to allow assertion libraries to throw errors directly into mocha. + * This is useful when running tests in a browser because window.onerror will + * only receive the 'message' attribute of the Error. + */ +mocha.throwError = function(err) { + Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) { + fn(err); + }); + throw err; +}; + +/** + * Override ui to ensure that the ui functions are initialized. + * Normally this would happen in Mocha.prototype.loadFiles. + */ + +mocha.ui = function(ui){ + Mocha.prototype.ui.call(this, ui); + this.suite.emit('pre-require', global, null, this); + return this; +}; + +/** + * Setup mocha with the given setting options. + */ + +mocha.setup = function(opts){ + if ('string' == typeof opts) opts = { ui: opts }; + for (var opt in opts) this[opt](opts[opt]); + return this; +}; + +/** + * Run mocha, returning the Runner. + */ + +mocha.run = function(fn){ + var options = mocha.options; + mocha.globals('location'); + + var query = Mocha.utils.parseQuery(global.location.search || ''); + if (query.grep) mocha.grep(new RegExp(query.grep)); + if (query.invert) mocha.invert(); + + return Mocha.prototype.run.call(mocha, function(err){ + // The DOM Document is not available in Web Workers. + var document = global.document; + if (document && document.getElementById('mocha') && options.noHighlighting !== true) { + Mocha.utils.highlightTags('code'); + } + if (fn) fn(err); + }); +}; + +/** + * Expose the process shim. + */ + +Mocha.process = process; +})(); diff --git a/package-lock.json b/package-lock.json index 40199ef..995ca79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,6 +94,11 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" }, + "fuse.js": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz", + "integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==" + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -104,6 +109,11 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "jaro-winkler": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/jaro-winkler/-/jaro-winkler-0.2.8.tgz", + "integrity": "sha1-Zyfg0LcJHiQ2+TVt6b+I+tI+U0o=" + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", diff --git a/package.json b/package.json index 5abe52f..fef095b 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "dependencies": { "discord.js": "^12.4.1", "fs": "0.0.1-security", + "fuse.js": "^6.4.6", + "jaro-winkler": "^0.2.8", "mongodb": "^3.6.3" }, "devDependencies": {}, From f116df5225c6d09f4cff2a438b81f5473d1f3edd Mon Sep 17 00:00:00 2001 From: GuyWhoCodes Date: Thu, 25 Mar 2021 21:19:00 -0700 Subject: [PATCH 02/38] test improved help embed --- Commands/pnda.js | 6 ++++-- globalfunctions.js | 24 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Commands/pnda.js b/Commands/pnda.js index 456c251..b096161 100644 --- a/Commands/pnda.js +++ b/Commands/pnda.js @@ -1,7 +1,9 @@ +const aliases = ["panda", "henry"] module.exports = { name: "pnda", - alises: ["panda", "henry", "Henry"], + alises: aliases, execute(message, args) { - message.channel.send("<@317751950441447435> stoopid pnda") + message.channel.send({embed: commandHelpEmbed("Pnda", aliases, Date.now(), "g!pnda", "Returns a ping for stoopid pnda")}) + // message.channel.send("<@317751950441447435> stoopid pnda") } } \ No newline at end of file diff --git a/globalfunctions.js b/globalfunctions.js index 1080a1d..f8edac8 100644 --- a/globalfunctions.js +++ b/globalfunctions.js @@ -57,7 +57,7 @@ module.exports = { }, ], footer: { - text: 'Skycomm Guide Bot', + text: 'Skyblock Guides', icon_url: "https://i.imgur.com/184jyne.png", }, timestamp: new Date() @@ -74,6 +74,26 @@ module.exports = { }, channelID(channel){ return channel.split("").slice(2,channel.length-1).join("") + }, + commandHelpEmbed(name, alias, time, example, result) { + return { + color: 0x4ea8de, + title: `${name}`, + fields: [ + { + name: 'Aliases', + value: `${alias.join(", ")}`, + }, + { + name: 'Example', + value: `${example}\n\u200b ${result}` + }, + ], + footer: { + text: 'Skyblock Guides', + icon_url: "https://i.imgur.com/184jyne.png", + }, + timestamp: time + } } - } \ No newline at end of file From 878591c3e610e8923764e34c40d23d0d90bbb363 Mon Sep 17 00:00:00 2001 From: GuyWhoCodes Date: Thu, 25 Mar 2021 21:20:33 -0700 Subject: [PATCH 03/38] forgot to reference --- Commands/pnda.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Commands/pnda.js b/Commands/pnda.js index b096161..d5b77d3 100644 --- a/Commands/pnda.js +++ b/Commands/pnda.js @@ -1,9 +1,10 @@ const aliases = ["panda", "henry"] +const globalFunctions = require("../globalfunctions.js") module.exports = { name: "pnda", alises: aliases, execute(message, args) { - message.channel.send({embed: commandHelpEmbed("Pnda", aliases, Date.now(), "g!pnda", "Returns a ping for stoopid pnda")}) + message.channel.send({embed: globalFunctions.commandHelpEmbed("Pnda", aliases, Date.now(), "g!pnda", "Returns a ping for stoopid pnda")}) // message.channel.send("<@317751950441447435> stoopid pnda") } } \ No newline at end of file From 3259ec18b87e0687a3b57cae87cbdc05757489a5 Mon Sep 17 00:00:00 2001 From: GuyWhoCodes Date: Thu, 25 Mar 2021 21:36:51 -0700 Subject: [PATCH 04/38] added more helpful support for default case --- Commands/dsuggest.js | 2 +- Commands/listcategories.js | 5 +++-- Commands/pnda.js | 9 +++------ Commands/sbsuggest.js | 2 +- globalfunctions.js | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Commands/dsuggest.js b/Commands/dsuggest.js index 20d2acd..1d7d34c 100644 --- a/Commands/dsuggest.js +++ b/Commands/dsuggest.js @@ -20,7 +20,7 @@ module.exports = { name: 'dsuggest', alises: ["suggestd", "dungeonsSuggest", "DungeonsSuggest", "DungeonSuggest", "dungeonsuggest", "dungeonsuggestion", "ds", "Ds", "DS"], execute(message, args) { - if (args.length == 0) return message.channel.send("You need to input a suggestion! See `g!dsuggest `") + if (args.length == 0) return message.channel.send({embed: globalFunctions.commandHelpEmbed("Dungeon Guide Suggestion", aliasList, Date.now(), "g!dsuggest My suggestion!", "Suggests 'my suggestion' to be considered for change on the Dungeon Guide")}) //checks if there is any bad input let userSuggestion = args.join(" ").trim() if (userSuggestion.length >= 1024) return message.channel.send("Your suggestion has hit the max character limit (1024). Shorten the suggestion or break up the suggestion into smaller suggestions.") diff --git a/Commands/listcategories.js b/Commands/listcategories.js index b3cdb80..8ef1c68 100644 --- a/Commands/listcategories.js +++ b/Commands/listcategories.js @@ -1,10 +1,11 @@ const {dbClient} = require("../mongodb.js") const {sbAlias, dAlias} = require("../constants.js") const globalFunctions = require("../globalfunctions.js") +const aliasList = ["lc", "list", "listc", "listcategory"] module.exports = { name: 'listcategories', - alises: ["lc", "list", "listc", "listC", "Listcategories", "listcategory", "Listcategory"], + alises: aliasList, async execute(message, args) { var listEmbed = { color: 0x4ea8de, @@ -20,7 +21,7 @@ module.exports = { } var guide = args[0] var categoryID = "" - if (args.length == 0 || guide == undefined) return message.channel.send('See `g!listcategories <#Guide Channel>`') + if (args.length == 0 || guide == undefined) return message.channel.send({embed: globalFunctions.commandHelpEmbed("List Categories", aliasList, Date.now(), "g!lc sb", "Returns all the category names for Skyblock Guides")}) //checks if there is any bad input if (globalFunctions.checkAliases(sbAlias, guide) == false && globalFunctions.checkAliases(dAlias, guide) == false) return message.channel.send('You are missing an argument! See `g!listcategories <#Guide Channel>`') diff --git a/Commands/pnda.js b/Commands/pnda.js index d5b77d3..9aa5c3e 100644 --- a/Commands/pnda.js +++ b/Commands/pnda.js @@ -1,10 +1,7 @@ -const aliases = ["panda", "henry"] -const globalFunctions = require("../globalfunctions.js") module.exports = { name: "pnda", - alises: aliases, - execute(message, args) { - message.channel.send({embed: globalFunctions.commandHelpEmbed("Pnda", aliases, Date.now(), "g!pnda", "Returns a ping for stoopid pnda")}) - // message.channel.send("<@317751950441447435> stoopid pnda") + alises: ["panda", "henry"], + execute(message, args) { + message.channel.send("<@317751950441447435> stoopid pnda") } } \ No newline at end of file diff --git a/Commands/sbsuggest.js b/Commands/sbsuggest.js index 739942a..5ee3e01 100644 --- a/Commands/sbsuggest.js +++ b/Commands/sbsuggest.js @@ -20,7 +20,7 @@ module.exports = { name: 'sbsuggest', alises: ["Sbsuggest", "suggestsb", "SkyblockSuggestion", "skyblockSuggestion", "skyblockSuggest", "SkyblockSuggest", "skyblocksuggest", "sbsug", "sbs"], execute(message, args) { - if (args.length == 0) return message.channel.send("You need to input a suggestion! See `g!sbsuggest `") + if (args.length == 0) return message.channel.send({embed: globalFunctions.commandHelpEmbed("Sb Guide Suggestion", aliasList, Date.now(), "g!sbsuggest My suggestion!", "Suggests 'my suggestion' to be considered for change on the Skyblock Guide")}) //checks if there is any bad input let userSuggestion = args.join(" ").trim() if (userSuggestion.length >= 1024) return message.channel.send("Your suggestion has hit the max character limit (1024). Shorten the suggestion or break up the suggestion into smaller suggestions.") diff --git a/globalfunctions.js b/globalfunctions.js index f8edac8..1ef1d0b 100644 --- a/globalfunctions.js +++ b/globalfunctions.js @@ -86,7 +86,7 @@ module.exports = { }, { name: 'Example', - value: `${example}\n\u200b ${result}` + value: `${example}\n\u200b${result}` }, ], footer: { From 1dec715902d7ff452bc9d9e44212a599128568a8 Mon Sep 17 00:00:00 2001 From: GuyWhoCodes Date: Thu, 25 Mar 2021 21:41:04 -0700 Subject: [PATCH 05/38] fixed copy paste error --- Commands/dsuggest.js | 4 ++-- Commands/sbsuggest.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Commands/dsuggest.js b/Commands/dsuggest.js index 1d7d34c..41de468 100644 --- a/Commands/dsuggest.js +++ b/Commands/dsuggest.js @@ -1,6 +1,6 @@ const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") - +const aliasList = ["suggestd", "dungeonsSuggest", "dungeonsuggest", "dungeonsuggestion", "ds"] var suggestEmbed = { color: 0xffba00, title: 'Suggestion', @@ -18,7 +18,7 @@ var suggestEmbed = { module.exports = { name: 'dsuggest', - alises: ["suggestd", "dungeonsSuggest", "DungeonsSuggest", "DungeonSuggest", "dungeonsuggest", "dungeonsuggestion", "ds", "Ds", "DS"], + alises: aliasList, execute(message, args) { if (args.length == 0) return message.channel.send({embed: globalFunctions.commandHelpEmbed("Dungeon Guide Suggestion", aliasList, Date.now(), "g!dsuggest My suggestion!", "Suggests 'my suggestion' to be considered for change on the Dungeon Guide")}) //checks if there is any bad input diff --git a/Commands/sbsuggest.js b/Commands/sbsuggest.js index 5ee3e01..346dd9c 100644 --- a/Commands/sbsuggest.js +++ b/Commands/sbsuggest.js @@ -1,6 +1,6 @@ const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") - +const aliasList = ["Sbsuggest", "suggestsb", "skyblockSuggestion", "skyblockSuggest", "sbsug", "sbs"] var suggestEmbed = { color: 0xffba00, title: 'Suggestion', @@ -18,7 +18,7 @@ var suggestEmbed = { module.exports = { name: 'sbsuggest', - alises: ["Sbsuggest", "suggestsb", "SkyblockSuggestion", "skyblockSuggestion", "skyblockSuggest", "SkyblockSuggest", "skyblocksuggest", "sbsug", "sbs"], + alises: aliasList, execute(message, args) { if (args.length == 0) return message.channel.send({embed: globalFunctions.commandHelpEmbed("Sb Guide Suggestion", aliasList, Date.now(), "g!sbsuggest My suggestion!", "Suggests 'my suggestion' to be considered for change on the Skyblock Guide")}) //checks if there is any bad input From 9efac931a0f78a435d600243375a133a1f6f9654 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:08:47 -0700 Subject: [PATCH 06/38] testing advanced mongodb search --- Commands/search.js | 34 +++++++++++++++++++++------------- globalfunctions.js | 3 +++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Commands/search.js b/Commands/search.js index 0504466..f85a118 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -5,30 +5,38 @@ module.exports = { name: "search", async execute(message, args) { if (args.length == 0) return message.channel.send("See `g!search `") - let searchQuery = args.join(" ").trim() + let searchQuery = globalFunctions.escapeRegex(args.join(" ").trim()) //checks if there is any bad input - var categoryName = new RegExp(searchQuery, "i") + // var categoryName = new RegExp(searchQuery, "i") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") - let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() - - if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") + // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() + let guide = await guidesDB.aggregate([{ + $search: { + "text": { + "query": searchQuery, + "path": ["categoryTitle", {"wildcard": "embedMessage.*"}] + } + } + }]) + console.log(guide) + // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database - let guideMessage = guide[0].embedMessage - guideMessage.timestamp = new Date() - message.channel.send({embed: guideMessage}).catch(err => { - message.channel.send("Oops! Something went wrong. Error Message: " + err) - }) + // let guideMessage = guide[0].embedMessage + // guideMessage.timestamp = new Date() + // message.channel.send({embed: guideMessage}).catch(err => { + // message.channel.send("Oops! Something went wrong. Error Message: " + err) + // }) } } // https://fusejs.io/ // https://www.npmjs.com/package/jaro-winkler -// https://docs.mongodb.com/drivers/node/usage-examples/findOne/ -// http://mongodb.github.io/node-mongodb-native/3.6/api/ // Command to run test file for this is: node ./Commands/search.js // const fuse = require('fuse.js') // const distance = require('jaro-winkler') // the distance from jaro-winkler should be first algo to use. fuse is backup algo. -// might need to pull from entire db? need to read into that \ No newline at end of file +// might need to pull from entire db? need to read into that +// https://docs.atlas.mongodb.com/reference/atlas-search/text/#std-label-text-ref +// https://docs.atlas.mongodb.com/reference/atlas-search/path-construction/#nested-field-example \ No newline at end of file diff --git a/globalfunctions.js b/globalfunctions.js index 1ef1d0b..6d0689c 100644 --- a/globalfunctions.js +++ b/globalfunctions.js @@ -95,5 +95,8 @@ module.exports = { }, timestamp: time } + }, + escapeRegex(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } } \ No newline at end of file From a8cdc16ea8ccf4cfba2c713f9910de5b9c325f67 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:10:34 -0700 Subject: [PATCH 07/38] outputted wrong thing --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index f85a118..c8d8c1d 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -18,7 +18,7 @@ module.exports = { } } }]) - console.log(guide) + console.log(guide[0].embedMessage) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database From fcd2c5f12df9ea4520ab8190896fc5996022c8ae Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:12:58 -0700 Subject: [PATCH 08/38] string error --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index c8d8c1d..fa5ecab 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -11,7 +11,7 @@ module.exports = { let guidesDB = dbClient.db("skyblockGuide").collection("Guides") // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() let guide = await guidesDB.aggregate([{ - $search: { + "$search": { "text": { "query": searchQuery, "path": ["categoryTitle", {"wildcard": "embedMessage.*"}] From 3e3c9dd413f82f33e8e1edc8ce06e33afc771f2c Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:35:47 -0700 Subject: [PATCH 09/38] added index on mongodb side --- Commands/search.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index fa5ecab..6406689 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -10,6 +10,7 @@ module.exports = { // var categoryName = new RegExp(searchQuery, "i") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() + // let guide = await guidesDB.find({$text: {$search: searchQuery}}) let guide = await guidesDB.aggregate([{ "$search": { "text": { @@ -18,7 +19,7 @@ module.exports = { } } }]) - console.log(guide[0].embedMessage) + console.log(guide) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database From 8f15ac24a9422c2f89c8ee0a5eb344ffacf6ce05 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:38:17 -0700 Subject: [PATCH 10/38] weird result --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index 6406689..3297843 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -19,7 +19,7 @@ module.exports = { } } }]) - console.log(guide) + console.log(guide[0]) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database From 0290c6fbe6b8ab5e21635d67e52dbd80b19cbf48 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 4 Apr 2021 10:40:12 -0700 Subject: [PATCH 11/38] something with string? --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index 3297843..d40d77d 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -12,7 +12,7 @@ module.exports = { // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() // let guide = await guidesDB.find({$text: {$search: searchQuery}}) let guide = await guidesDB.aggregate([{ - "$search": { + $search: { "text": { "query": searchQuery, "path": ["categoryTitle", {"wildcard": "embedMessage.*"}] From 4d4628b6390deb199c683fe5796c01a243e25e32 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 15:12:56 -0700 Subject: [PATCH 12/38] pulling whole db for custom search test --- Commands/search.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Commands/search.js b/Commands/search.js index d40d77d..5e18780 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -10,16 +10,8 @@ module.exports = { // var categoryName = new RegExp(searchQuery, "i") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() - // let guide = await guidesDB.find({$text: {$search: searchQuery}}) - let guide = await guidesDB.aggregate([{ - $search: { - "text": { - "query": searchQuery, - "path": ["categoryTitle", {"wildcard": "embedMessage.*"}] - } - } - }]) - console.log(guide[0]) + let guide = await guidesDB.find({}).toArray() + console.log(guide) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database @@ -38,6 +30,4 @@ module.exports = { // const distance = require('jaro-winkler') // the distance from jaro-winkler should be first algo to use. fuse is backup algo. -// might need to pull from entire db? need to read into that -// https://docs.atlas.mongodb.com/reference/atlas-search/text/#std-label-text-ref -// https://docs.atlas.mongodb.com/reference/atlas-search/path-construction/#nested-field-example \ No newline at end of file +// might need to pull from entire db? need to read into that \ No newline at end of file From 56d209e6305060704af74c6f7511aa3f1c9adb0a Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 18:15:19 -0700 Subject: [PATCH 13/38] implementation of jaro-winkler search algo --- Commands/search.js | 47 +++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/Commands/search.js b/Commands/search.js index 5e18780..b659833 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -1,33 +1,54 @@ const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") - +const distance = require('jaro-winkler') module.exports = { name: "search", async execute(message, args) { if (args.length == 0) return message.channel.send("See `g!search `") let searchQuery = globalFunctions.escapeRegex(args.join(" ").trim()) //checks if there is any bad input - // var categoryName = new RegExp(searchQuery, "i") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") - // let guide = await guidesDB.find( { "categoryTitle": { $regex: categoryName } }).toArray() let guide = await guidesDB.find({}).toArray() - console.log(guide) + + let threshold = 0.60 + const parseQuery = query => { + possibleQueries = {} + guide.map(val => { + let closeness = val.categoryTitle + .split(" ") + .map(word => distance(query, word, {caseSensitive: false})) + .reduce((prev, current) => prev + current)/(val.categoryTitle.split(" ").length) + if (closeness > threshold) { + possibleQueries[val.categoryTitle] = closeness + possibleQueries[val.categoryTitle + " embed"] = val.embedMessage + } + }) + + var bestQuery = "" + Object.keys(possibleQueries) + .filter(val => !val.includes("embed")) + .map(val => { + if (bestQuery == "" || possibleQueries[bestQuery] < possibleQueries[val]) { + bestQuery = val + } + }) + return possibleQueries[bestQuery + " embed"] + } + let guideMessage = parseQuery(searchQuery) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database - // let guideMessage = guide[0].embedMessage - // guideMessage.timestamp = new Date() - // message.channel.send({embed: guideMessage}).catch(err => { - // message.channel.send("Oops! Something went wrong. Error Message: " + err) - // }) + + guideMessage.timestamp = new Date() + message.channel.send({embed: guideMessage}).catch(err => { + message.channel.send("Oops! Something went wrong. Error Message: " + err) + }) } } // https://fusejs.io/ // https://www.npmjs.com/package/jaro-winkler -// Command to run test file for this is: node ./Commands/search.js // const fuse = require('fuse.js') -// const distance = require('jaro-winkler') +//fuse is backup algo. -// the distance from jaro-winkler should be first algo to use. fuse is backup algo. -// might need to pull from entire db? need to read into that \ No newline at end of file +// Command to run test file for this is: node ./Commands/search.js \ No newline at end of file From 8bdd283cfe05324d07c41bdfc7e8b438668d10d4 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 21:26:41 -0700 Subject: [PATCH 14/38] testing fuse search results --- Commands/search.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Commands/search.js b/Commands/search.js index b659833..5b503e6 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -1,6 +1,14 @@ const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") const distance = require('jaro-winkler') +const Fuse = require('fuse.js') +const options = { + includeScore: true, + shouldSort: true, + keys: ["categoryTitle", "embedMessage.fields.name"] +} +let threshold = 0.60 + module.exports = { name: "search", async execute(message, args) { @@ -9,8 +17,8 @@ module.exports = { //checks if there is any bad input let guidesDB = dbClient.db("skyblockGuide").collection("Guides") let guide = await guidesDB.find({}).toArray() - - let threshold = 0.60 + let fuseSearch = new Fuse(guide, options) + const parseQuery = query => { possibleQueries = {} guide.map(val => { @@ -18,21 +26,23 @@ module.exports = { .split(" ") .map(word => distance(query, word, {caseSensitive: false})) .reduce((prev, current) => prev + current)/(val.categoryTitle.split(" ").length) + if (closeness > threshold) { possibleQueries[val.categoryTitle] = closeness possibleQueries[val.categoryTitle + " embed"] = val.embedMessage } }) - - var bestQuery = "" + + console.log(fuse.search(query)) + var bestResult = "" Object.keys(possibleQueries) .filter(val => !val.includes("embed")) .map(val => { - if (bestQuery == "" || possibleQueries[bestQuery] < possibleQueries[val]) { - bestQuery = val + if (bestResult == "" || possibleQueries[bestResult] < possibleQueries[val]) { + bestResult = val } }) - return possibleQueries[bestQuery + " embed"] + return possibleQueries[bestResult + " embed"] } let guideMessage = parseQuery(searchQuery) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") @@ -47,8 +57,7 @@ module.exports = { } // https://fusejs.io/ -// https://www.npmjs.com/package/jaro-winkler -// const fuse = require('fuse.js') + //fuse is backup algo. // Command to run test file for this is: node ./Commands/search.js \ No newline at end of file From d07423d9f8b6de4ddef43ce7af7e95a2669e29af Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 21:28:22 -0700 Subject: [PATCH 15/38] name error --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index 5b503e6..2119af2 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -33,7 +33,7 @@ module.exports = { } }) - console.log(fuse.search(query)) + console.log(fuseSearch.search(query)) var bestResult = "" Object.keys(possibleQueries) .filter(val => !val.includes("embed")) From bfc0c42d12ce6a24298f949296601d4e1b993d3e Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 21:42:11 -0700 Subject: [PATCH 16/38] implemented fuzzy search algo backup --- Commands/search.js | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/Commands/search.js b/Commands/search.js index 2119af2..2ef4826 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -13,8 +13,8 @@ module.exports = { name: "search", async execute(message, args) { if (args.length == 0) return message.channel.send("See `g!search `") - let searchQuery = globalFunctions.escapeRegex(args.join(" ").trim()) //checks if there is any bad input + let searchQuery = globalFunctions.escapeRegex(args.join(" ").trim()) let guidesDB = dbClient.db("skyblockGuide").collection("Guides") let guide = await guidesDB.find({}).toArray() let fuseSearch = new Fuse(guide, options) @@ -32,32 +32,33 @@ module.exports = { possibleQueries[val.categoryTitle + " embed"] = val.embedMessage } }) + //Implements the Jaro-winkler search algorithm by comparing the search query string to the guide's title + + if (Object.keys(possibleQueries).length != 0) { + var bestResult = "" + Object.keys(possibleQueries) + .filter(val => !val.includes("embed")) + .map(val => { + if (bestResult == "" || possibleQueries[bestResult] < possibleQueries[val]) { + bestResult = val + } + }) + return possibleQueries[bestResult + " embed"] + } + //Sorts out the results by picking the highest rated one and returns the corresponding guide embed message - console.log(fuseSearch.search(query)) - var bestResult = "" - Object.keys(possibleQueries) - .filter(val => !val.includes("embed")) - .map(val => { - if (bestResult == "" || possibleQueries[bestResult] < possibleQueries[val]) { - bestResult = val - } - }) - return possibleQueries[bestResult + " embed"] + let results = fuseSearch.search(query) + return results[0].embedMessage + //Implements fuzzy searching as a backup search algorithm when the Jaro-winkler algorithm doesn't give a result } + let guideMessage = parseQuery(searchQuery) // if (guide[0] == undefined || guide.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database - guideMessage.timestamp = new Date() message.channel.send({embed: guideMessage}).catch(err => { message.channel.send("Oops! Something went wrong. Error Message: " + err) }) } -} - -// https://fusejs.io/ - -//fuse is backup algo. - -// Command to run test file for this is: node ./Commands/search.js \ No newline at end of file +} \ No newline at end of file From 91a4ff4aa2865a93f85f8b7ec3940ec0b891128f Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Tue, 6 Apr 2021 21:43:50 -0700 Subject: [PATCH 17/38] returned wrong scope --- Commands/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/search.js b/Commands/search.js index 2ef4826..cc2a0d7 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -48,7 +48,7 @@ module.exports = { //Sorts out the results by picking the highest rated one and returns the corresponding guide embed message let results = fuseSearch.search(query) - return results[0].embedMessage + return results[0].item.embedMessage //Implements fuzzy searching as a backup search algorithm when the Jaro-winkler algorithm doesn't give a result } From e4aff9ed1e2854b3d1a5bdcd0df3041730523008 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Wed, 7 Apr 2021 09:03:08 -0700 Subject: [PATCH 18/38] removed deprecated code and unnecessary aliases --- Commands/addcategory.js | 27 ++++++++------------------- Commands/addsection.js | 2 +- Commands/approve.js | 2 +- Commands/dsuggest.js | 2 +- Commands/sbsuggest.js | 2 +- Commands/search.js | 6 ++++-- constants.js | 1 - 7 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Commands/addcategory.js b/Commands/addcategory.js index 4834838..3cefdf8 100644 --- a/Commands/addcategory.js +++ b/Commands/addcategory.js @@ -1,5 +1,5 @@ const {dbClient} = require("../mongodb.js") -const {sbAlias, dAlias, uAlias} = require("../constants.js") +const {sbAlias, dAlias} = require("../constants.js") const globalFunctions = require("../globalfunctions.js") var categoryEmbed = { @@ -12,14 +12,14 @@ var categoryEmbed = { value: "_ _", }], footer: { - text: 'Skycomm Guide Bot', + text: 'Skyblock Guides', icon_url: "https://i.imgur.com/184jyne.png", }, } module.exports = { name: "addcategory", - alises: ["ac", "addc", "AC", "Ac", "addC", "Addcategory", "AddCategory"], + alises: ["ac", "addc"], execute(message, args) { var categoryChannel = "" var msgID = "" @@ -28,7 +28,7 @@ module.exports = { if (args.length == 0 || category.length == 0 || categoryName.length == 0) return message.channel.send("See `g!addcategory <#Guide Channel> `") //checks if there is any bad input - if (globalFunctions.checkAliases(sbAlias, category) == false && globalFunctions.checkAliases(dAlias, category) == false && globalFunctions.checkAliases(uAlias, category) == false) return message.channel.send("You are missing an argument! Please use the right format. `g!addcategory <#Guide Channel> `") + if (globalFunctions.checkAliases(sbAlias, category) == false && globalFunctions.checkAliases(dAlias, category) == false) return message.channel.send("You are missing an argument! Please use the right format. `g!addcategory <#Guide Channel> `") //checks if provided alias does not match list of alises categoryEmbed.title = categoryName @@ -46,26 +46,15 @@ module.exports = { categoryEmbed.color = 0xcc0000 categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!dsuggest `!" - } else if (globalFunctions.checkAliases(uAlias, category)) { - category = "Update Tips" - categoryChannel = message.guild.channels.cache.find(ch => ch.name === "update-tips") - categoryEmbed.color = 0xffba00 - categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!update `!" - } + } categoryChannel.send({ embed: categoryEmbed }).then(msg => msgID = msg.id) let database = dbClient.db("skyblockGuide") let guideDB = database.collection("Guides") - let updateDB = database.collection("Update Tips") let newEntry = globalFunctions.makeNewEntry(categoryEmbed, categoryName, msgID, category) - if (category == "Update Tips") { - updateDB.insertOne(newEntry) - updateDB.updateOne({"identifier": category}, {$set: {"identifier": category, "currentMsgId": msgID, "msgObject": categoryEmbed}}) - } else { - guideDB.insertOne(newEntry) - } - + guideDB.insertOne(newEntry) + message.channel.send("Your category has been created!") - } + } } \ No newline at end of file diff --git a/Commands/addsection.js b/Commands/addsection.js index f891137..21e4864 100644 --- a/Commands/addsection.js +++ b/Commands/addsection.js @@ -7,7 +7,7 @@ const entrySchema = { module.exports = { name: "addsection", - alises: ["as", "Addsection", "Adds", "AddSection", "As"], + alises: ["as", "adds"], async execute(message, args) { let sectionName = args.slice(1, args.length).join(" ").trim() if (args.length == 0 || args[0] == undefined || sectionName.length == 0) return message.channel.send("See `g!addsection
    `") diff --git a/Commands/approve.js b/Commands/approve.js index 0f2db93..b512324 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -7,7 +7,7 @@ const capitalizeString = str => { module.exports = { name: 'approve', - alises: ["a", "Approve"], + alises: ["a"], async execute(message, args) { if (args.length == 0 || args[1] == undefined || args[2] == undefined) return message.channel.send("See `g!approve `") //checks if there is any bad input diff --git a/Commands/dsuggest.js b/Commands/dsuggest.js index 41de468..da65113 100644 --- a/Commands/dsuggest.js +++ b/Commands/dsuggest.js @@ -11,7 +11,7 @@ var suggestEmbed = { value: "_ _", }], footer: { - text: 'Skycomm Guide Bot', + text: 'Skyblock Guides', icon_url: "https://i.imgur.com/184jyne.png", }, } diff --git a/Commands/sbsuggest.js b/Commands/sbsuggest.js index 346dd9c..ed42b8d 100644 --- a/Commands/sbsuggest.js +++ b/Commands/sbsuggest.js @@ -11,7 +11,7 @@ var suggestEmbed = { value: "_ _", }], footer: { - text: 'Skycomm Guide Bot', + text: 'Skyblock Guides', icon_url: "https://i.imgur.com/184jyne.png", }, } diff --git a/Commands/search.js b/Commands/search.js index cc2a0d7..c96bc83 100644 --- a/Commands/search.js +++ b/Commands/search.js @@ -7,12 +7,14 @@ const options = { shouldSort: true, keys: ["categoryTitle", "embedMessage.fields.name"] } -let threshold = 0.60 +const threshold = 0.60 +const aliasList = ["query", "s"] module.exports = { name: "search", + alises: aliasList, async execute(message, args) { - if (args.length == 0) return message.channel.send("See `g!search `") + if (args.length == 0) return globalFunctions.commandHelpEmbed("Search", aliasList, Date.now(), "g!search money", "Returns the Common Money Making Guide") //checks if there is any bad input let searchQuery = globalFunctions.escapeRegex(args.join(" ").trim()) let guidesDB = dbClient.db("skyblockGuide").collection("Guides") diff --git a/constants.js b/constants.js index 6b83d39..8831246 100644 --- a/constants.js +++ b/constants.js @@ -12,7 +12,6 @@ const categorySchema = { } const sbAlias = ["<#772942075301068820>", "sb", "skyblock", 'Skyblock', 'SB', 'SkyBlock'] const dAlias = ["<#772944394542121031>","d", "dungeons", "dung", "Dungeons", "D", "dungeon", "Dungeon", "Dung"] -const uAlias = ["u", "update", "UPDATE", "Update", "U"] const restrictedCmds = ['addcategory', 'addsection', 'approve', 'delete', 'edit', 'post', 'start', 'pnda', 'ad'] const verifiedRoles = ['Verified','VIP', 'VIP+', 'MVP', 'MVP+', 'MVP++'] const cooldownCmds = ['sbsuggest', 'dsuggest', 'update', 'start'] From a85e9d2abb70b9f2497c0ac4e44f90e91b9ac64c Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Wed, 7 Apr 2021 09:18:12 -0700 Subject: [PATCH 19/38] forgot to remove dependency --- constants.js | 1 - 1 file changed, 1 deletion(-) diff --git a/constants.js b/constants.js index 8831246..ece5012 100644 --- a/constants.js +++ b/constants.js @@ -43,7 +43,6 @@ module.exports = { categorySchema: categorySchema, sbAlias: sbAlias, dAlias: dAlias, - uAlias: uAlias, yesAlias: yesAlias, noAlias: noAlias, cancelAlias: cancelAlias, From c4df24afdf3e1b2408cdf0796506c8d1832d80f6 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 15:15:11 -0700 Subject: [PATCH 20/38] beta test of command helper --- Commands/approve.js | 69 +++++++++++++++++++++++++++++++++++++++------ Commands/config.js | 2 +- Commands/help.js | 10 +++---- server.js | 1 - 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index b512324..bd37f24 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -1,4 +1,4 @@ -const {dbClient} = require("../mongodb.js") +// const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") const capitalizeString = str => { @@ -9,20 +9,66 @@ module.exports = { name: 'approve', alises: ["a"], async execute(message, args) { - if (args.length == 0 || args[1] == undefined || args[2] == undefined) return message.channel.send("See `g!approve `") - //checks if there is any bad input + var messageID, categoryTitle, sectionTitle, suggestion = "" + if (!args.includes("-")) { + var suggestionConfirm = false + var categoryConfirm = false + var sectionConfirm = false + const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 + const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) - var messageID = args[0] - var categoryTitle = globalFunctions.translateCategoryName(args[1]) - var sectionTitle = globalFunctions.translateCategoryName(args[2]) + // ["Suggestion ID", "", "Section Name (Smaller bold)"] + message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Suggestion ID.") + collector.on('collect', msg => { + if (suggestionConfirm && categoryConfirm && sectionConfirm) { + collector.stop() + return undefined + + } else if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())) { + collector.stop() + message.channel.send("Process canceled.") + return undefined + //stops process if given no/cancel alias + } + if (suggestionConfirm && categoryConfirm && !sectionConfirm) { + //confirmation for all settings + sectionConfirm = true + if (msg.content.includes("#")) { + configEmbed.fields[3].value = channel + } + } else if (suggestionConfirm && !categoryConfirm) { + //Suggestion ID confirmed + + + } else if (!suggestionConfirm) { + //Suggestion confirmed + let findSuggestion = await suggestionDB.find({"messageID": msg.trim()}).toArray() + if (findSuggestion.length == 0) return message.channel.send("The given message ID did not match anything in the database. Enter a new one.") + //returns an error if the provided message ID did not match anything in the database + + if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") + //returns an error if the retrieved message from the database was already approved + + messageID = msg.trim() + suggestionConfirm = true + message.channel.send("Enter the Category Name (Bold name at the top of the embed message).") + //if the suggestion is found in the database, run this portion of the code + } + }) + } + //Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly + + messageID = args[0] + categoryTitle = globalFunctions.translateCategoryName(args[1]) + sectionTitle = globalFunctions.translateCategoryName(args[2]) if (args.length >= 4) return message.channel.send("I received more parameters (>3) than I can work with. If there are more than 2 words in the Category or Section name, replace the spaces with a hyphen (-).") //returns an error if Category name or Section Name is not formatted correctly let suggestionDB = dbClient.db("skyblockGuide").collection("suggestions") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") - let suggestion = await suggestionDB.find({"messageID": messageID}).toArray() + suggestion = await suggestionDB.find({"messageID": messageID}).toArray() - if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve
    `") + if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve `") //returns an error if the provided message ID did not match anything in the database if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") //returns an error if the retrieved message from the database was already approved @@ -55,6 +101,10 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit + + + + let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { @@ -77,4 +127,5 @@ module.exports = { suggestionDB.updateOne({"messageID": messageID}, {$set: {"section": suggestion[0].section, "messageID": messageID, "description": suggestion[0].description, "user": suggestion[0].user, "status": "Approved"}}) message.channel.send("That suggestion has been approved!") }, -} \ No newline at end of file +} +//testing: node ./Commands/approve.js \ No newline at end of file diff --git a/Commands/config.js b/Commands/config.js index cf69838..b4da5cf 100644 --- a/Commands/config.js +++ b/Commands/config.js @@ -100,7 +100,7 @@ module.exports = { collector.stop() message.channel.send("Process canceled.") return undefined - //stops Edit process if given no/cancel alias + //stops process if given no/cancel alias } else if (filter(msg)) { let channel = msg.content.trim() diff --git a/Commands/help.js b/Commands/help.js index e4e3501..af37f2a 100644 --- a/Commands/help.js +++ b/Commands/help.js @@ -16,7 +16,7 @@ module.exports = { }, { name: '`g!search `', - value: 'Searches the guides that best match your query. **Recommended** to do `g!listcategories <#Guide Channel>` first to narrow search.\n\u200b', + value: 'Searches the guides that best match your query.\n\u200b', }, { name: '`g!sbsuggest `', @@ -28,19 +28,19 @@ module.exports = { }, { name: '`g!addcategory <#Guide Channel> `', - value: 'Adds an embed for people to add changes in their respective channels.\n\u200b', + value: 'Adds a new Guide message to their respective channels.\n\u200b', }, { name: '`g!addsection
    `', - value: 'Adds a new subtitle to a Category Message.\n\u200b', + value: 'Adds a new Guide Section to a Category Message.\n\u200b', }, { name: '`g!approve
    `', - value: 'Approves a suggestion made in <#772944441643630602> and edits the message in the Skyblock or Dungeons Guide.\n\u200b', + value: 'Approves a suggestion made in <#772944441643630602> by editing the Skyblock or Dungeons Guide message.\n\u200b', }, { name: '`g!delete <#Channel>`', - value: 'Deletes suggestions from <#772944441643630602> and deletes specific sections from any of the Guides.\n\u200b', + value: 'Deletes suggestions from <#772944441643630602> and specific sections from any of the Guides.\n\u200b', }, { name: '`g!edit <#Channel>`', diff --git a/server.js b/server.js index b8a0b52..a7f3f6b 100644 --- a/server.js +++ b/server.js @@ -108,7 +108,6 @@ client.on('message', async (message) => { } catch (error) { message.channel.send("There was an error in excuting that command.") - message.channel.send("Error message: " + error) } }) From 1b327105d9670c7eae67418c70d7b041fdc1c9a4 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 15:18:06 -0700 Subject: [PATCH 21/38] scope issue --- Commands/approve.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index bd37f24..32c43b1 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -10,6 +10,9 @@ module.exports = { alises: ["a"], async execute(message, args) { var messageID, categoryTitle, sectionTitle, suggestion = "" + let suggestionDB = dbClient.db("skyblockGuide").collection("suggestions") + let guidesDB = dbClient.db("skyblockGuide").collection("Guides") + if (!args.includes("-")) { var suggestionConfirm = false var categoryConfirm = false @@ -64,10 +67,8 @@ module.exports = { if (args.length >= 4) return message.channel.send("I received more parameters (>3) than I can work with. If there are more than 2 words in the Category or Section name, replace the spaces with a hyphen (-).") //returns an error if Category name or Section Name is not formatted correctly - let suggestionDB = dbClient.db("skyblockGuide").collection("suggestions") - let guidesDB = dbClient.db("skyblockGuide").collection("Guides") suggestion = await suggestionDB.find({"messageID": messageID}).toArray() - + if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve `") //returns an error if the provided message ID did not match anything in the database if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") @@ -104,7 +105,7 @@ module.exports = { - + let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { From a3e61b957481590a0ec7d3e455719db35bec480f Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 15:19:32 -0700 Subject: [PATCH 22/38] startup error --- Commands/approve.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 32c43b1..7b116d4 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -22,7 +22,7 @@ module.exports = { // ["Suggestion ID", "", "Section Name (Smaller bold)"] message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Suggestion ID.") - collector.on('collect', msg => { + collector.on('collect', async(msg) => { if (suggestionConfirm && categoryConfirm && sectionConfirm) { collector.stop() return undefined @@ -68,7 +68,7 @@ module.exports = { //returns an error if Category name or Section Name is not formatted correctly suggestion = await suggestionDB.find({"messageID": messageID}).toArray() - + if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve `") //returns an error if the provided message ID did not match anything in the database if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") From b1dfb0a39ab70826ad2d870d8687741d7ddb9ebe Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 15:23:19 -0700 Subject: [PATCH 23/38] forgot to uncomment --- Commands/approve.js | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 7b116d4..871ca01 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -1,4 +1,4 @@ -// const {dbClient} = require("../mongodb.js") +const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") const capitalizeString = str => { @@ -58,54 +58,54 @@ module.exports = { //if the suggestion is found in the database, run this portion of the code } }) - } + } //Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly - messageID = args[0] - categoryTitle = globalFunctions.translateCategoryName(args[1]) - sectionTitle = globalFunctions.translateCategoryName(args[2]) - if (args.length >= 4) return message.channel.send("I received more parameters (>3) than I can work with. If there are more than 2 words in the Category or Section name, replace the spaces with a hyphen (-).") - //returns an error if Category name or Section Name is not formatted correctly - - suggestion = await suggestionDB.find({"messageID": messageID}).toArray() - - if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve `") - //returns an error if the provided message ID did not match anything in the database - if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") - //returns an error if the retrieved message from the database was already approved - let categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(categoryTitle, "i") } }).toArray() - let embedMessage = categoryMsg[0].embedMessage - - if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect. Remember to separate Category titles with more than 2 words with hyphens.") - //returns an error if the Category Title did not match anything in the database - - var foundSection = false - var approveMsgIndex = 0 - var fieldError = false - embedMessage.fields.map((val, index) => { - if (val.name.toLowerCase() === sectionTitle.toLowerCase()) { - foundSection = true - approveMsgIndex = index - if ((val.value.length + suggestion[0].description.length + "\n\u200b".length) > 1024) fieldError = true - //edhe case field char limit detection - val.value === "_ _" ? val.value = suggestion[0].description + "\n\u200b": val.value += "\n\u200b" + suggestion[0].description + "\n\u200b" - } - }) - //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") - - if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") - //returns an error if the provided Section Name did not match anything in the Guide message - if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") - //edge case when the suggestion trying to be approved is in the wrong section - if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") - //edge case when embed exceeds limit - if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") - //edge case when field value exceeds character limit - + else { + messageID = args[0] + categoryTitle = globalFunctions.translateCategoryName(args[1]) + sectionTitle = globalFunctions.translateCategoryName(args[2]) + if (args.length >= 4) return message.channel.send("I received more parameters (>3) than I can work with. If there are more than 2 words in the Category or Section name, replace the spaces with a hyphen (-).") + //returns an error if Category name or Section Name is not formatted correctly + suggestion = await suggestionDB.find({"messageID": messageID}).toArray() + if (suggestion.length == 0) return message.channel.send("The given message ID was copied wrong. Please use the right format. `g!approve `") + //returns an error if the provided message ID did not match anything in the database + if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") + //returns an error if the retrieved message from the database was already approved + let categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(categoryTitle, "i") } }).toArray() + let embedMessage = categoryMsg[0].embedMessage + + if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect. Remember to separate Category titles with more than 2 words with hyphens.") + //returns an error if the Category Title did not match anything in the database + + var foundSection = false + var approveMsgIndex = 0 + var fieldError = false + embedMessage.fields.map((val, index) => { + if (val.name.toLowerCase() === sectionTitle.toLowerCase()) { + foundSection = true + approveMsgIndex = index + if ((val.value.length + suggestion[0].description.length + "\n\u200b".length) > 1024) fieldError = true + //edhe case field char limit detection + val.value === "_ _" ? val.value = suggestion[0].description + "\n\u200b": val.value += "\n\u200b" + suggestion[0].description + "\n\u200b" + } + }) + //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") + if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") + //returns an error if the provided Section Name did not match anything in the Guide message + if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") + //edge case when the suggestion trying to be approved is in the wrong section + if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") + //edge case when embed exceeds limit + if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") + //edge case when field value exceeds character limit + } + + let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { From 500c3bba2574c0e1a429c031d46c75f2599fb1c5 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 15:26:13 -0700 Subject: [PATCH 24/38] forgot to import --- Commands/approve.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Commands/approve.js b/Commands/approve.js index 871ca01..1664bc3 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -1,10 +1,11 @@ const {dbClient} = require("../mongodb.js") const globalFunctions = require("../globalfunctions.js") - +const {noAlias, cancelAlias} = require("../constants.js") const capitalizeString = str => { return str[0].toUpperCase() + str.substring(1) } + module.exports = { name: 'approve', alises: ["a"], From 869ae4d01a914e26e7684dfdc21faec6196e47ed Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 16:15:05 -0700 Subject: [PATCH 25/38] testing complete argument helper --- Commands/approve.js | 84 ++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 1664bc3..fb1dfee 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -10,7 +10,7 @@ module.exports = { name: 'approve', alises: ["a"], async execute(message, args) { - var messageID, categoryTitle, sectionTitle, suggestion = "" + var messageID, categoryTitle, sectionTitle, suggestion, categoryMsg, embedMessage = "" let suggestionDB = dbClient.db("skyblockGuide").collection("suggestions") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") @@ -21,46 +21,66 @@ module.exports = { const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) - // ["Suggestion ID", "", "Section Name (Smaller bold)"] + // ["Suggestion ID", "", ""] message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Suggestion ID.") collector.on('collect', async(msg) => { - if (suggestionConfirm && categoryConfirm && sectionConfirm) { - collector.stop() - return undefined - - } else if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())) { + if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ collector.stop() message.channel.send("Process canceled.") return undefined //stops process if given no/cancel alias - } - if (suggestionConfirm && categoryConfirm && !sectionConfirm) { - //confirmation for all settings + + } else if (suggestionConfirm && categoryConfirm && !sectionConfirm) { + var foundSection = false + var approveMsgIndex = 0 + var fieldError = false + embedMessage.fields.map((val, index) => { + if (val.name.toLowerCase() === sectionTitle.toLowerCase()) { + foundSection = true + approveMsgIndex = index + if ((val.value.length + suggestion[0].description.length + "\n\u200b".length) > 1024) fieldError = true + //edhe case field char limit detection + val.value === "_ _" ? val.value = suggestion[0].description + "\n\u200b": val.value += "\n\u200b" + suggestion[0].description + "\n\u200b" + } + }) + //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") + if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") + //returns an error if the provided Section Name did not match anything in the Guide message + sectionConfirm = true - if (msg.content.includes("#")) { - configEmbed.fields[3].value = channel - } + collector.stop() + } else if (suggestionConfirm && !categoryConfirm) { - //Suggestion ID confirmed - + categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() + if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect.") + //returns an error if the Category Title did not match anything in the database + + embedMessage = categoryMsg[0].embedMessage + categoryConfirm = true + message.channel.send("Enter the Section Name (Smaller bold).") + return undefined + //if the category message is found in the database, run this portion of the code } else if (!suggestionConfirm) { - //Suggestion confirmed - let findSuggestion = await suggestionDB.find({"messageID": msg.trim()}).toArray() - if (findSuggestion.length == 0) return message.channel.send("The given message ID did not match anything in the database. Enter a new one.") + + suggestion = await suggestionDB.find({"messageID": globalFunctions.translateCategoryName(msg.content.trim()) }).toArray() + if (suggestion.length == 0) return message.channel.send("The given message ID did not match anything in the database. Enter a new one.") //returns an error if the provided message ID did not match anything in the database if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") //returns an error if the retrieved message from the database was already approved - messageID = msg.trim() + messageID = msg.content.trim() suggestionConfirm = true message.channel.send("Enter the Category Name (Bold name at the top of the embed message).") + return undefined //if the suggestion is found in the database, run this portion of the code } }) } - //Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly + //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly + //Exits out of the code to prevent the code below the argument parsing from returning an error + else { messageID = args[0] @@ -75,8 +95,8 @@ module.exports = { //returns an error if the provided message ID did not match anything in the database if (suggestion[0].status === "Approved") return message.channel.send("The suggestion was already approved!") //returns an error if the retrieved message from the database was already approved - let categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(categoryTitle, "i") } }).toArray() - let embedMessage = categoryMsg[0].embedMessage + categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(categoryTitle, "i") } }).toArray() + embedMessage = categoryMsg[0].embedMessage if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect. Remember to separate Category titles with more than 2 words with hyphens.") //returns an error if the Category Title did not match anything in the database @@ -94,19 +114,20 @@ module.exports = { } }) //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") - if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message - if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") - //edge case when the suggestion trying to be approved is in the wrong section - if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") - //edge case when embed exceeds limit - if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") - //edge case when field value exceeds character limit } - + //**Default command.** Format: g!approve + if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") + //edge case when the suggestion trying to be approved is in the wrong section + if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") + //edge case when embed exceeds limit + if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") + //edge case when field value exceeds character limit + + return message.channel.send("Everything has worked up to this point!") let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { @@ -129,5 +150,4 @@ module.exports = { suggestionDB.updateOne({"messageID": messageID}, {$set: {"section": suggestion[0].section, "messageID": messageID, "description": suggestion[0].description, "user": suggestion[0].user, "status": "Approved"}}) message.channel.send("That suggestion has been approved!") }, -} -//testing: node ./Commands/approve.js \ No newline at end of file +} \ No newline at end of file From 6dbb0c961cee04b21aa1c9cd3b11a56900073e4f Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 16:25:17 -0700 Subject: [PATCH 26/38] fixed edge case not working --- Commands/approve.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index fb1dfee..5466396 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -15,9 +15,7 @@ module.exports = { let guidesDB = dbClient.db("skyblockGuide").collection("Guides") if (!args.includes("-")) { - var suggestionConfirm = false - var categoryConfirm = false - var sectionConfirm = false + var suggestionConfirm, categoryConfirm = false const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) @@ -30,7 +28,7 @@ module.exports = { return undefined //stops process if given no/cancel alias - } else if (suggestionConfirm && categoryConfirm && !sectionConfirm) { + } else if (suggestionConfirm && categoryConfirm) { var foundSection = false var approveMsgIndex = 0 var fieldError = false @@ -47,12 +45,11 @@ module.exports = { if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message - sectionConfirm = true collector.stop() } else if (suggestionConfirm && !categoryConfirm) { categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() - if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect.") + if (categoryMsg.length == 0 || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect.") //returns an error if the Category Title did not match anything in the database embedMessage = categoryMsg[0].embedMessage From 49fc51c8765ae0f47149a2f09917f0cf131b71d0 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 16:30:03 -0700 Subject: [PATCH 27/38] copy paste error --- Commands/approve.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 5466396..a83280b 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -13,13 +13,14 @@ module.exports = { var messageID, categoryTitle, sectionTitle, suggestion, categoryMsg, embedMessage = "" let suggestionDB = dbClient.db("skyblockGuide").collection("suggestions") let guidesDB = dbClient.db("skyblockGuide").collection("Guides") + var foundSection, fieldError, suggestionConfirm, categoryConfirm = false + var approveMsgIndex = 0 if (!args.includes("-")) { - var suggestionConfirm, categoryConfirm = false const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) - // ["Suggestion ID", "", ""] + message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Suggestion ID.") collector.on('collect', async(msg) => { if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ @@ -29,11 +30,8 @@ module.exports = { //stops process if given no/cancel alias } else if (suggestionConfirm && categoryConfirm) { - var foundSection = false - var approveMsgIndex = 0 - var fieldError = false embedMessage.fields.map((val, index) => { - if (val.name.toLowerCase() === sectionTitle.toLowerCase()) { + if (val.name.toLowerCase() === globalFunctions.translateCategoryName(msg.content.trim()).toLowerCase()) { foundSection = true approveMsgIndex = index if ((val.value.length + suggestion[0].description.length + "\n\u200b".length) > 1024) fieldError = true @@ -98,9 +96,6 @@ module.exports = { if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Title that was given was incorrect. Remember to separate Category titles with more than 2 words with hyphens.") //returns an error if the Category Title did not match anything in the database - var foundSection = false - var approveMsgIndex = 0 - var fieldError = false embedMessage.fields.map((val, index) => { if (val.name.toLowerCase() === sectionTitle.toLowerCase()) { foundSection = true From 89b5fe3d61272bebb882c75321d9acab98e154b3 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 16:43:32 -0700 Subject: [PATCH 28/38] testing if embed message works --- Commands/approve.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Commands/approve.js b/Commands/approve.js index a83280b..68861d8 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -42,7 +42,7 @@ module.exports = { //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message - + sectionTitle = globalFunctions.translateCategoryName(msg.content.trim()) collector.stop() } else if (suggestionConfirm && !categoryConfirm) { @@ -51,7 +51,9 @@ module.exports = { //returns an error if the Category Title did not match anything in the database embedMessage = categoryMsg[0].embedMessage + console.log(embedMessage) categoryConfirm = true + categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) message.channel.send("Enter the Section Name (Smaller bold).") return undefined //if the category message is found in the database, run this portion of the code From 3b91213dc6e2e9814b0a738fa2f86a12c05306bc Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 16:47:10 -0700 Subject: [PATCH 29/38] testing final cases --- Commands/approve.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Commands/approve.js b/Commands/approve.js index 68861d8..a852e27 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -121,7 +121,9 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit - return message.channel.send("Everything has worked up to this point!") + message.channel.send("Everything has worked up to this point!") + console.log("Everything has worked up to this point!") + return undefined let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { From 1bd008087d0e7a9d8b876bd0d3e67d6b715af597 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 19:08:41 -0700 Subject: [PATCH 30/38] debug messages --- Commands/approve.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Commands/approve.js b/Commands/approve.js index a852e27..955c584 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -39,10 +39,12 @@ module.exports = { val.value === "_ _" ? val.value = suggestion[0].description + "\n\u200b": val.value += "\n\u200b" + suggestion[0].description + "\n\u200b" } }) + console.log("Test Suggestion " + suggestion[0]) //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message sectionTitle = globalFunctions.translateCategoryName(msg.content.trim()) + console.log("Test Message") collector.stop() } else if (suggestionConfirm && !categoryConfirm) { @@ -51,7 +53,6 @@ module.exports = { //returns an error if the Category Title did not match anything in the database embedMessage = categoryMsg[0].embedMessage - console.log(embedMessage) categoryConfirm = true categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) message.channel.send("Enter the Section Name (Smaller bold).") From 7ea5656d60dd81a07a9c4b521b68daa14293618d Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 19:13:25 -0700 Subject: [PATCH 31/38] fixed scope error --- Commands/approve.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 955c584..bc4ad1e 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -39,12 +39,17 @@ module.exports = { val.value === "_ _" ? val.value = suggestion[0].description + "\n\u200b": val.value += "\n\u200b" + suggestion[0].description + "\n\u200b" } }) - console.log("Test Suggestion " + suggestion[0]) //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message sectionTitle = globalFunctions.translateCategoryName(msg.content.trim()) - console.log("Test Message") + + if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") + //edge case when the suggestion trying to be approved is in the wrong section + if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") + //edge case when embed exceeds limit + if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") + //edge case when field value exceeds character limit collector.stop() } else if (suggestionConfirm && !categoryConfirm) { @@ -60,7 +65,6 @@ module.exports = { //if the category message is found in the database, run this portion of the code } else if (!suggestionConfirm) { - suggestion = await suggestionDB.find({"messageID": globalFunctions.translateCategoryName(msg.content.trim()) }).toArray() if (suggestion.length == 0) return message.channel.send("The given message ID did not match anything in the database. Enter a new one.") //returns an error if the provided message ID did not match anything in the database @@ -111,20 +115,17 @@ module.exports = { //adds the suggestion message to the existing Guide Message by looping through all the fields for matching Section name and adding new line at the end ("\n\u200b") if (foundSection == false) return message.channel.send("The section that was given was incorrect. Remember to separate Section titles with more than 2 words with hyphens.") //returns an error if the provided Section Name did not match anything in the Guide message + if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") + //edge case when the suggestion trying to be approved is in the wrong section + if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") + //edge case when embed exceeds limit + if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") + //edge case when field value exceeds character limit } //**Default command.** Format: g!approve - - if (suggestion[0].section != categoryMsg[0].category || capitalizeString(suggestion[0].section) != categoryMsg[0].category) return message.channel.send("The suggestion that you have tried to approve does not match with the category's guide. Make sure that Skyblock Suggestions are approved for the Skyblock Guide and that Dungeon Suggestions are approved for the Dungeons Guide.") - //edge case when the suggestion trying to be approved is in the wrong section - if (globalFunctions.embedCharCount(categoryMsg[0]) >= 6000) return message.channel.send("Error. Approving the following suggestion exceeds the embed character limit (6000). Use `g!e` to shorten the embed.") - //edge case when embed exceeds limit - if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") - //edge case when field value exceeds character limit - - message.channel.send("Everything has worked up to this point!") console.log("Everything has worked up to this point!") - return undefined + return message.channel.send("Everything has worked up to this point!") let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { From 3fb01a80e598132fe12f7ff86af87b9b23820603 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 19:21:18 -0700 Subject: [PATCH 32/38] fixed code running out of intended scope --- Commands/approve.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Commands/approve.js b/Commands/approve.js index bc4ad1e..da60689 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -79,6 +79,7 @@ module.exports = { //if the suggestion is found in the database, run this portion of the code } }) + return undefined } //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly //Exits out of the code to prevent the code below the argument parsing from returning an error From c94e877bf228c0551254f70501ca19a92b157d72 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 19:29:05 -0700 Subject: [PATCH 33/38] testing scope changes and added test server --- Commands/approve.js | 4 ++++ server.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Commands/approve.js b/Commands/approve.js index da60689..c06fb33 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -16,6 +16,7 @@ module.exports = { var foundSection, fieldError, suggestionConfirm, categoryConfirm = false var approveMsgIndex = 0 + argumentChecker: { if (!args.includes("-")) { const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) @@ -51,6 +52,8 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit collector.stop() + break argumentChecker; + //exits scope of the Argument checker to run the rest of the command } else if (suggestionConfirm && !categoryConfirm) { categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() @@ -123,6 +126,7 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit } + } //**Default command.** Format: g!approve console.log("Everything has worked up to this point!") diff --git a/server.js b/server.js index a7f3f6b..cb1c16a 100644 --- a/server.js +++ b/server.js @@ -67,7 +67,8 @@ client.on('message', async (message) => { try { let userCmd = client.commands.get(command) || client.commands.find(cmd => cmd.alises && cmd.alises.includes(command)) - if (message.guild.id != "587765474297905158") { + if (message.guild.id != "587765474297905158" && message.guild.id != "807319824752443472") { + //Remove command restriction on Skycomm (home server) and Test Server (private test server) if (globalFunction.checkAliases(nonSkycommCmds, userCmd.name)) { userCmd.execute(message, args) } else { From 0a1a88d77fb5892ea82359896c7563eae913ee61 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 19:33:31 -0700 Subject: [PATCH 34/38] fixed scope issue? --- Commands/approve.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index c06fb33..2d25627 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -16,7 +16,6 @@ module.exports = { var foundSection, fieldError, suggestionConfirm, categoryConfirm = false var approveMsgIndex = 0 - argumentChecker: { if (!args.includes("-")) { const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) @@ -52,8 +51,6 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit collector.stop() - break argumentChecker; - //exits scope of the Argument checker to run the rest of the command } else if (suggestionConfirm && !categoryConfirm) { categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() @@ -82,7 +79,9 @@ module.exports = { //if the suggestion is found in the database, run this portion of the code } }) - return undefined + + if (!suggestionConfirm) return undefined + message.channel.send("Everything's alright up to this point!") } //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly //Exits out of the code to prevent the code below the argument parsing from returning an error @@ -126,7 +125,6 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit } - } //**Default command.** Format: g!approve console.log("Everything has worked up to this point!") From c6bf9f412b032618e705a465339e38cea62a93d2 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Sun, 11 Apr 2021 21:38:47 -0700 Subject: [PATCH 35/38] testing scope fix --- Commands/approve.js | 5 ++--- server.js | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Commands/approve.js b/Commands/approve.js index 2d25627..8702e19 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -16,11 +16,10 @@ module.exports = { var foundSection, fieldError, suggestionConfirm, categoryConfirm = false var approveMsgIndex = 0 - if (!args.includes("-")) { + if (args.length == 0) { const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) - message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Suggestion ID.") collector.on('collect', async(msg) => { if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ @@ -51,6 +50,7 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit collector.stop() + return message.channel.send("Everything has worked up to this point!") } else if (suggestionConfirm && !categoryConfirm) { categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() @@ -81,7 +81,6 @@ module.exports = { }) if (!suggestionConfirm) return undefined - message.channel.send("Everything's alright up to this point!") } //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly //Exits out of the code to prevent the code below the argument parsing from returning an error diff --git a/server.js b/server.js index cb1c16a..c9b778c 100644 --- a/server.js +++ b/server.js @@ -59,7 +59,6 @@ client.on('message', async (message) => { if (!cooldowns.has("sbsuggest")) cooldowns.set("sbsuggest", new Discord.Collection()) if (!cooldowns.has("dsuggest")) cooldowns.set("dsuggest", new Discord.Collection()) - if (!cooldowns.has("update")) cooldowns.set("update", new Discord.Collection()) if (!cooldowns.has("start")) cooldowns.set("start", new Discord.Collection()) const now = Date.now() From 4c7a3761e8ca079beb8e1896aebd51cdb0861659 Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Mon, 12 Apr 2021 08:08:43 -0700 Subject: [PATCH 36/38] testing add category argument helper --- Commands/addcategory.js | 97 +++++++++++++++++++++++++++++++++-------- Commands/addsection.js | 64 +++++++++++++++++++++++++++ Commands/approve.js | 27 ++++++++++-- 3 files changed, 167 insertions(+), 21 deletions(-) diff --git a/Commands/addcategory.js b/Commands/addcategory.js index 3cefdf8..afbd54e 100644 --- a/Commands/addcategory.js +++ b/Commands/addcategory.js @@ -5,7 +5,7 @@ const globalFunctions = require("../globalfunctions.js") var categoryEmbed = { color: 0xffba00, title: 'Suggestion', - description: "This is an empty section of the guide. Add some changes to this guide using!", + description: "This is an empty section of the guide.", fields: [ { name: '_ _', @@ -21,17 +21,80 @@ module.exports = { name: "addcategory", alises: ["ac", "addc"], execute(message, args) { - var categoryChannel = "" - var msgID = "" - var category = args[0] - let categoryName = args.slice(1, args.length).join(" ").trim() - if (args.length == 0 || category.length == 0 || categoryName.length == 0) return message.channel.send("See `g!addcategory <#Guide Channel> `") - //checks if there is any bad input - - if (globalFunctions.checkAliases(sbAlias, category) == false && globalFunctions.checkAliases(dAlias, category) == false) return message.channel.send("You are missing an argument! Please use the right format. `g!addcategory <#Guide Channel> `") - //checks if provided alias does not match list of alises + var categoryChannel, msgID, category, categoryName = "" + var channelConfirm = false + let guideDB = dbClient.db("skyblockGuide").collection("Guides") - categoryEmbed.title = categoryName + + if (args.length == 0) { + const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 + const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) + + message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Channel to put the new Guide in: `skyblock` or `dungeons`") + collector.on('collect', async(msg) => { + if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ + collector.stop() + message.channel.send("Process canceled.") + return undefined + //stops process if given no/cancel alias + + } else if (channelConfirm) { + + categoryConfirm = true + categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) + + collector.stop() + //Stops prompting the user + return message.channel.send("Your category has been created!") + + categoryEmbed.title = categoryName + categoryEmbed.timestamp = new Date() + + if (globalFunctions.checkAliases(sbAlias, category)) { + category = "Skyblock" + categoryChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-guide") + categoryEmbed.color = 0x87d8fa + categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!sbsuggest `!" + + } else if (globalFunctions.checkAliases(dAlias, category)) { + category = "Dungeons" + categoryChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") + categoryEmbed.color = 0xcc0000 + categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!dsuggest `!" + + } + + + //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 78-93 is copied and pasted here. + + } else if (!channelConfirm) { + if (globalFunctions.checkAliases(sbAlias, msg.content.trim()) == false && globalFunctions.checkAliases(dAlias, msg.content.trim()) == false) return message.channel.send("Incorrect input. Type in a Guide Channel or its corresponding Alias.") + //checks if provided alias does not match list of alises + + channelConfirm = true + category = msg.content.trim() + message.channel.send("Enter the name of the new Guide (Category).") + return undefined + //if a valid alias of the guide channel is given, run this portion of the code + } + }) + + if (!channelConfirm) return undefined + } + //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly + //Exits out of the code to prevent the code below the argument parsing from returning an error + + else { + category = args[0] + categoryName = args.slice(1, args.length).join(" ").trim() + if (globalFunctions.checkAliases(sbAlias, category) == false && globalFunctions.checkAliases(dAlias, category) == false) return message.channel.send("You are missing an argument! Please use the right format. `g!addcategory <#Guide Channel> `") + //checks if provided alias does not match list of alises + + } + //**Default command.** Format: g!ac <#Guide-Channel> + + return message.channel.send("Severe error if code reaches here in argument helper") + categoryEmbed.title = categoryName categoryEmbed.timestamp = new Date() if (globalFunctions.checkAliases(sbAlias, category)) { @@ -45,16 +108,14 @@ module.exports = { categoryChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") categoryEmbed.color = 0xcc0000 categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!dsuggest `!" - + } - - categoryChannel.send({ embed: categoryEmbed }).then(msg => msgID = msg.id) - let database = dbClient.db("skyblockGuide") - let guideDB = database.collection("Guides") + categoryChannel.send({ embed: categoryEmbed }).then(msg => msgID = msg.id) + let newEntry = globalFunctions.makeNewEntry(categoryEmbed, categoryName, msgID, category) guideDB.insertOne(newEntry) - - message.channel.send("Your category has been created!") + + message.channel.send("Your category has been created!") } } \ No newline at end of file diff --git a/Commands/addsection.js b/Commands/addsection.js index 21e4864..95d9371 100644 --- a/Commands/addsection.js +++ b/Commands/addsection.js @@ -12,6 +12,67 @@ module.exports = { let sectionName = args.slice(1, args.length).join(" ").trim() if (args.length == 0 || args[0] == undefined || sectionName.length == 0) return message.channel.send("See `g!addsection
    `") //checks if there is any bad input + + + if (args.length == 0) { + const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 + const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) + + message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Channel to put the new Guide in: `skyblock` or `dungeons`") + collector.on('collect', async(msg) => { + if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ + collector.stop() + message.channel.send("Process canceled.") + return undefined + //stops process if given no/cancel alias + + } else if (channelConfirm) { + + categoryConfirm = true + categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) + + collector.stop() + //Stops prompting the user + + categoryEmbed.title = categoryName + categoryEmbed.timestamp = new Date() + + if (globalFunctions.checkAliases(sbAlias, category)) { + category = "Skyblock" + categoryChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-guide") + categoryEmbed.color = 0x87d8fa + categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!sbsuggest `!" + + } else if (globalFunctions.checkAliases(dAlias, category)) { + category = "Dungeons" + categoryChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") + categoryEmbed.color = 0xcc0000 + categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!dsuggest `!" + + } + + return message.channel.send("Your category has been created!") + //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 78-93 is copied and pasted here. + + } else if (!channelConfirm) { + if (globalFunctions.checkAliases(sbAlias, msg.content.trim()) == false && globalFunctions.checkAliases(dAlias, msg.content.trim()) == false) return message.channel.send("Incorrect input. Type in a Guide Channel or its corresponding Alias.") + //checks if provided alias does not match list of alises + + channelConfirm = true + category = msg.content.trim() + message.channel.send("Enter the name of the new Guide (Category).") + return undefined + //if a valid alias of the guide channel is given, run this portion of the code + } + }) + + if (!channelConfirm) return undefined + } + //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly + //Exits out of the code to prevent the code below the argument parsing from returning an error + + + var categoryName = globalFunction.translateCategoryName(args[0]) let guideCollection = dbClient.db("skyblockGuide").collection("Guides") @@ -19,6 +80,9 @@ module.exports = { if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Name provided did not match anything. Did you make sure to include hyphens?") //If the provided category does not exist in the database, give the user an error saying so. + //**Default command.** Format: g!as + + let msgEmbed = categoryMsg[0].embedMessage msgEmbed.timestamp = new Date() // include edge case here to remove default value diff --git a/Commands/approve.js b/Commands/approve.js index 8702e19..672ac62 100644 --- a/Commands/approve.js +++ b/Commands/approve.js @@ -50,7 +50,30 @@ module.exports = { if (fieldError) return message.channel.send("Error. Approving the following suggestion exceeds the field character limit (1024). Use `g!e` to shorten the embed.") //edge case when field value exceeds character limit collector.stop() - return message.channel.send("Everything has worked up to this point!") + //Stops prompting the user + + let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") + suggestionChannel.messages.fetch({around: messageID, limit: 1}) + .then(msg => { + msg.first().edit("This suggestion has been approved!") + }) + + var guideChannel = "" + embedMessage.timestamp = new Date() + categoryMsg[0].category === "Skyblock" ? guideChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-guide") : guideChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") + guideChannel.messages.fetch({around: messageID, limit: 1}) + .then(msg => { + msg.first().edit({embed: embedMessage}).then(me => {message.channel.send("ID: " + me.id)}); + }) + + guidesDB.updateOne({"categoryTitle": { $regex: new RegExp(categoryTitle, "i") }}, {$set: {"embedMessage": embedMessage, "categoryTitle": categoryMsg[0].categoryTitle, "messageID": messageID, "category": categoryMsg[0].category}}) + + let logChannel = message.guild.channels.cache.find(ch => ch.name === "guide-log") + logChannel.send({embed: globalFunctions.logAction(message.author.username, message.author.id, 'Approve', embedMessage.fields[approveMsgIndex].value, categoryMsg[0].categoryTitle)}) + + suggestionDB.updateOne({"messageID": messageID}, {$set: {"section": suggestion[0].section, "messageID": messageID, "description": suggestion[0].description, "user": suggestion[0].user, "status": "Approved"}}) + return message.channel.send("That suggestion has been approved!") + //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 153-173 is copied and pasted here. } else if (suggestionConfirm && !categoryConfirm) { categoryMsg = await guidesDB.find({"categoryTitle": { $regex: new RegExp(globalFunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() @@ -126,8 +149,6 @@ module.exports = { } //**Default command.** Format: g!approve - console.log("Everything has worked up to this point!") - return message.channel.send("Everything has worked up to this point!") let suggestionChannel = message.guild.channels.cache.find(ch => ch.name === "suggested-guide-changes") suggestionChannel.messages.fetch({around: messageID, limit: 1}) .then(msg => { From ea55680de6c01a8385dc811e725badde1520657d Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Mon, 12 Apr 2021 08:32:39 -0700 Subject: [PATCH 37/38] forgot to import constants --- Commands/addcategory.js | 2 +- Commands/addsection.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Commands/addcategory.js b/Commands/addcategory.js index afbd54e..e6e9875 100644 --- a/Commands/addcategory.js +++ b/Commands/addcategory.js @@ -1,5 +1,5 @@ const {dbClient} = require("../mongodb.js") -const {sbAlias, dAlias} = require("../constants.js") +const {sbAlias, dAlias, noAlias, cancelAlias} = require("../constants.js") const globalFunctions = require("../globalfunctions.js") var categoryEmbed = { diff --git a/Commands/addsection.js b/Commands/addsection.js index 95d9371..d9a6f82 100644 --- a/Commands/addsection.js +++ b/Commands/addsection.js @@ -1,5 +1,6 @@ const {dbClient} = require("../mongodb.js") const globalFunction = require("../globalfunctions.js") +const {noAlias, cancelAlias} = require("../constants.js") const entrySchema = { "name": "_ _", "value": "_ _" From b16046674f3d3d826ab0532ab43fd407b6460f4b Mon Sep 17 00:00:00 2001 From: GuyWhoCode Date: Mon, 12 Apr 2021 09:51:46 -0700 Subject: [PATCH 38/38] finalized args helper for addcategory and section --- Commands/addcategory.js | 12 +++--- Commands/addsection.js | 83 ++++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/Commands/addcategory.js b/Commands/addcategory.js index e6e9875..b2299c4 100644 --- a/Commands/addcategory.js +++ b/Commands/addcategory.js @@ -39,13 +39,10 @@ module.exports = { //stops process if given no/cancel alias } else if (channelConfirm) { - - categoryConfirm = true categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) collector.stop() //Stops prompting the user - return message.channel.send("Your category has been created!") categoryEmbed.title = categoryName categoryEmbed.timestamp = new Date() @@ -64,8 +61,11 @@ module.exports = { } - - //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 78-93 is copied and pasted here. + categoryChannel.send({ embed: categoryEmbed }).then(msg => msgID = msg.id) + + let newEntry = globalFunctions.makeNewEntry(categoryEmbed, categoryName, msgID, category) + guideDB.insertOne(newEntry) + //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 98-118 is copied and pasted here. } else if (!channelConfirm) { if (globalFunctions.checkAliases(sbAlias, msg.content.trim()) == false && globalFunctions.checkAliases(dAlias, msg.content.trim()) == false) return message.channel.send("Incorrect input. Type in a Guide Channel or its corresponding Alias.") @@ -93,7 +93,7 @@ module.exports = { } //**Default command.** Format: g!ac <#Guide-Channel> - return message.channel.send("Severe error if code reaches here in argument helper") + categoryEmbed.title = categoryName categoryEmbed.timestamp = new Date() diff --git a/Commands/addsection.js b/Commands/addsection.js index d9a6f82..a84d88f 100644 --- a/Commands/addsection.js +++ b/Commands/addsection.js @@ -1,6 +1,7 @@ const {dbClient} = require("../mongodb.js") const globalFunction = require("../globalfunctions.js") const {noAlias, cancelAlias} = require("../constants.js") +const globalfunctions = require("../globalfunctions.js") const entrySchema = { "name": "_ _", "value": "_ _" @@ -10,16 +11,15 @@ module.exports = { name: "addsection", alises: ["as", "adds"], async execute(message, args) { - let sectionName = args.slice(1, args.length).join(" ").trim() - if (args.length == 0 || args[0] == undefined || sectionName.length == 0) return message.channel.send("See `g!addsection
    `") - //checks if there is any bad input - + var sectionName, categoryName, categoryMsg = "" + let guideCollection = dbClient.db("skyblockGuide").collection("Guides") + var categoryConfirm = false if (args.length == 0) { const filter = msg => msg.author.id === message.author.id && msg.content.length != 0 const collector = message.channel.createMessageCollector(filter, {time: globalFunctions.timeToMS("3m")}) - message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Channel to put the new Guide in: `skyblock` or `dungeons`") + message.channel.send("To cancel the Argument helper, type in `no` or `cancel`. Enter the Guide Category to add a new section.") collector.on('collect', async(msg) => { if (globalFunctions.checkAliases(noAlias, msg.content.trim()) || globalFunctions.checkAliases(cancelAlias, msg.content.trim())){ collector.stop() @@ -27,60 +27,65 @@ module.exports = { return undefined //stops process if given no/cancel alias - } else if (channelConfirm) { - - categoryConfirm = true - categoryTitle = globalFunctions.translateCategoryName(msg.content.trim()) + } else if (categoryConfirm) { + sectionName = globalFunctions.translateCategoryName(msg.content.trim()) collector.stop() //Stops prompting the user - categoryEmbed.title = categoryName - categoryEmbed.timestamp = new Date() - - if (globalFunctions.checkAliases(sbAlias, category)) { - category = "Skyblock" - categoryChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-guide") - categoryEmbed.color = 0x87d8fa - categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!sbsuggest `!" + let msgEmbed = categoryMsg[0].embedMessage + msgEmbed.timestamp = new Date() + // include edge case here to remove default value + + var newEntry = Object.create(entrySchema) + newEntry.name = sectionName + msgEmbed.fields.push(newEntry) - } else if (globalFunctions.checkAliases(dAlias, category)) { - category = "Dungeons" - categoryChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") - categoryEmbed.color = 0xcc0000 - categoryEmbed.description = "This is an empty section of the guide. Add some changes to this guide using `g!dsuggest `!" + delete msgEmbed.description + let channelName = categoryMsg[0].category + guideCollection.updateOne({"categoryTitle": { $regex: new RegExp(categoryName, "i") }}, {$set: {"category": channelName, "messageID": categoryMsg[0].messageID, "categoryTitle": categoryMsg[0].categoryTitle, "embedMessage": msgEmbed}}) + + var guideChannel = "" + channelName === "Skyblock" ? guideChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-guide") : guideChannel = message.guild.channels.cache.find(ch => ch.name === "dungeons-guide-n-tips") + if (categoryMsg[0].category === "resource") guideChannel = message.guild.channels.cache.find(ch => ch.name === "skyblock-resources") + + guideChannel.messages.fetch({around: categoryMsg[0].messageID, limit: 1}) + .then(msg => { + msg.first().edit({embed: msgEmbed}) + }) - } + return message.channel.send("Your section has been added!") + //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 95-112 is copied and pasted here. - return message.channel.send("Your category has been created!") - //Since Discord.js does not like exitting out of the Message collector after ending it, the same code from lines 78-93 is copied and pasted here. + } else if (!categoryConfirm) { - } else if (!channelConfirm) { - if (globalFunctions.checkAliases(sbAlias, msg.content.trim()) == false && globalFunctions.checkAliases(dAlias, msg.content.trim()) == false) return message.channel.send("Incorrect input. Type in a Guide Channel or its corresponding Alias.") - //checks if provided alias does not match list of alises + categoryMsg = await guideCollection.find({"categoryTitle": { $regex: new RegExp(globalfunctions.translateCategoryName(msg.content.trim()), "i") } }).toArray() + if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Name provided did not match anything, please enter another one.") - channelConfirm = true - category = msg.content.trim() - message.channel.send("Enter the name of the new Guide (Category).") + categoryConfirm = true + categoryName = msg.content.trim() + message.channel.send("Enter the name of the new Guide Section.") return undefined //if a valid alias of the guide channel is given, run this portion of the code } }) - if (!channelConfirm) return undefined + if (!categoryConfirm) return undefined } //**Enable the Argument helper: Prompts the user for each argument of the command to make it more user-friendly //Exits out of the code to prevent the code below the argument parsing from returning an error - - var categoryName = globalFunction.translateCategoryName(args[0]) + else { + if (args[0] == undefined || sectionName.length == 0) return message.channel.send("See `g!addsection
    `") + //checks if there is any bad input - let guideCollection = dbClient.db("skyblockGuide").collection("Guides") - let categoryMsg = await guideCollection.find({"categoryTitle": { $regex: new RegExp(categoryName, "i") } }).toArray() - if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Name provided did not match anything. Did you make sure to include hyphens?") - //If the provided category does not exist in the database, give the user an error saying so. - + categoryName = globalFunction.translateCategoryName(args[0]) + sectionName = args.slice(1, args.length).join(" ").trim() + categoryMsg = await guideCollection.find({"categoryTitle": { $regex: new RegExp(categoryName, "i") } }).toArray() + if (categoryMsg[0] == undefined || categoryMsg.length > 1) return message.channel.send("The Category Name provided did not match anything. Did you make sure to include hyphens?") + //If the provided category does not exist in the database, give the user an error saying so. + } //**Default command.** Format: g!as